[OHIF-Viewers]医疗数字阅片-医学影像-cornerstone-core-Cornerstone.js-Cornerstone Examples-基石实例-下
[OHIF-Viewers]医疗数字阅片-医学影像-cornerstone-core-Cornerstone.js-Cornerstone Examples-基石实例-下
CT Image with Window presets
This is an example of displaying a ct image along with buttons to set ww/wc presets. You can manually adjust the ww/wc by dragging the left mouse button
这是一个显示ct图像以及设置ww/wc预设的按钮的示例。可以通过拖动鼠标左键手动调整ww/wc
<!-- include special code for these examples which provides images --> <script src="../exampleImageIdLoaderCt.js"></script> <script> // image enable the dicomImage element const element = document.getElementById('dicomImage'); cornerstone.enable(element); // load and display the image const imageId = 'ctexample://1'; cornerstone.loadImage(imageId).then(function(image) { cornerstone.displayImage(element, image); const viewport = cornerstone.getViewport(element); document.getElementById('window').textContent = "WW/WC:" + Math.round(viewport.voi.windowWidth) + "/" + Math.round(viewport.voi.windowCenter); // Add event handler for the ww/wc presets document.getElementById('softTissue').addEventListener('click', function() { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth = 400; viewport.voi.windowCenter = 20; cornerstone.setViewport(element, viewport); document.getElementById('window').textContent = "WW/WC:" + Math.round(viewport.voi.windowWidth) + "/" + Math.round(viewport.voi.windowCenter); }); document.getElementById('lung').addEventListener('click', function() { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth = 1600; viewport.voi.windowCenter = -600; cornerstone.setViewport(element, viewport); document.getElementById('window').textContent = "WW/WC:" + Math.round(viewport.voi.windowWidth) + "/" + Math.round(viewport.voi.windowCenter); }); document.getElementById('bone').addEventListener('click', function() { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth = 2000; viewport.voi.windowCenter = 300; cornerstone.setViewport(element, viewport); document.getElementById('window').textContent = "WW/WC:" + Math.round(viewport.voi.windowWidth) + "/" + Math.round(viewport.voi.windowCenter); }); // add event handlers to mouse move to adjust window/center element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); document.getElementById('window').textContent = "WW/WC:" + Math.round(viewport.voi.windowWidth) + "/" + Math.round(viewport.voi.windowCenter); } function mouseUpHandler() { document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); }); </script>
Non-square pixels
This example shows an image with non square pixels. The image is 128x256 and has a column and row pixel spacing of 1.0/0.5 respectively. The image will display a square if non square pixels is being applied properly or a tall rectangle if not.
这个例子显示了一个非正方形像素的图像。图像为128x256,列像素间距为1.0/0.5。如果非正方形像素被正确应用,图像将显示一个正方形;如果没有,则显示一个高矩形。
<script> // Loads an image given an imageId function loadImage(imageId) { var width = 128; var height = 256; var numPixels = width * height; var pixelData = new Uint16Array(numPixels); var index = 0; // clear image to black for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { pixelData[index] = 128; index++; } } var left = 10; var top = 20; var squareWidth = 50; var squareHeight = 100; for (var row = top; row < top + squareHeight; row++) { var rowOffset = row * width; pixelData[rowOffset + left] = 255; pixelData[rowOffset + left + squareWidth] = 255; } var topRowOffset = top * width; var bottomRowOffset = (top + squareHeight) * width; for (var column = left; column < left + squareWidth; column++) { pixelData[topRowOffset + column] = 255; pixelData[bottomRowOffset + column] = 255; } function getPixelData() { return pixelData; } var image = { imageId: imageId, minPixelValue: 0, maxPixelValue: 255, slope: 1.0, intercept: 0, windowCenter: 127, windowWidth: 256, render: cornerstone.renderGrayscaleImage, getPixelData: getPixelData, rows: height, columns: width, height: height, width: width, color: false, columnPixelSpacing: 1.0, rowPixelSpacing: 0.5, invert: false, sizeInBytes: width * height * 2 }; // Create a Promise, resolve it with the image object we just created and return the // Promise to cornerstone. Cornerstone will get the image object by calling then() on the // Promise. An optional function can be provided to allow the image loader to cancel a request. return { promise: new Promise((resolve) => { resolve(image); }), cancelFn: undefined }; } cornerstone.registerImageLoader('myImageLoader', loadImage); // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); // load and display the image const imageId = "myImageLoader://1"; cornerstone.loadImage(imageId).then(function(image) { element.addEventListener("cornerstoneimagerendered", function(e) { const eventData = e.detail; // reset to identity matrix eventData.canvasContext.setTransform(1, 0, 0, 1, 0, 0); var context = eventData.canvasContext; context.beginPath(); context.strokeStyle = 'white'; context.lineWidth = 0; var topLeft = cornerstone.pixelToCanvas(element, {x:30, y:30}); var bottomRight = cornerstone.pixelToCanvas(element, {x:40, y:40}); context.rect(topLeft.x, topLeft.y, bottomRight.x-topLeft.x, bottomRight.y - topLeft.y); context.stroke(); context.fillStyle = "white"; context.font = "14px Arial"; context.fillText("Tumor Here", topLeft.x, topLeft.y); }); cornerstone.displayImage(element, image); }); // Add event handlers to flip or rotate the image document.getElementById('hFlip').addEventListener('click', function (e) { var viewport = cornerstone.getViewport(element); viewport.hflip = !viewport.hflip; cornerstone.setViewport(element, viewport); }); document.getElementById('vFlip').addEventListener('click', function (e) { var viewport = cornerstone.getViewport(element); viewport.vflip = !viewport.vflip; cornerstone.setViewport(element, viewport); }); document.getElementById('lRotate').addEventListener('click', function (e) { var viewport = cornerstone.getViewport(element); viewport.rotation-=90; cornerstone.setViewport(element, viewport); }); document.getElementById('rRotate').addEventListener('click', function (e) { var viewport = cornerstone.getViewport(element); viewport.rotation+=90; cornerstone.setViewport(element, viewport); }); document.getElementById('reset').addEventListener('click', function (e) { cornerstone.reset(element); }); element.addEventListener('mousemove', function(event) { const pixelCoords = cornerstone.pageToPixel(element, event.pageX, event.pageY); const pt = cornerstone.pixelToCanvas(element, pixelCoords); document.getElementById('coords').textContent = "page=(" + event.pageX + ", " + event.pageY + "); pixel=(" + pixelCoords.x.toFixed(1) + ", " + pixelCoords.y.toFixed(1) + '); canvas=(' + pt.x.toFixed(1) + ', ' + pt.y.toFixed(1) + ')'; }); // add event handlers to pan image on mouse move element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; const viewport = cornerstone.getViewport(element); viewport.translation.x += (deltaX / viewport.scale); viewport.translation.y += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } function mouseUpHandler() { document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); const mouseWheelEvents = ['mousewheel', 'DOMMouseScroll']; mouseWheelEvents.forEach(function(eventType) { element.addEventListener(eventType, function (e) { // Firefox e.detail > 0 scroll back, < 0 scroll forward // chrome/safari e.wheelDelta < 0 scroll back, > 0 scroll forward let viewport = cornerstone.getViewport(element); if (e.wheelDelta < 0 || e.detail > 0) { viewport.scale -= 0.25; } else { viewport.scale += 0.25; } cornerstone.setViewport(element, viewport); // Prevent page from scrolling return false; }); }); </script>
Color Images
This example shows how cornerstone can display color images. It uses a custom image loader that returns a color image
这个例子展示了基石如何显示彩色图像。它使用一个自定义的图像加载器返回一个彩色图像
<script> const canvas = document.createElement('canvas'); // Loads an image given an imageId function loadImage(imageId) { const width = 256; const height = 256; canvas.width = width; canvas.height = height; const canvasContext = canvas.getContext('2d'); const imageData = canvasContext.createImageData(width, height); const pixelData = imageData.data; const rnd = Math.round(Math.random() * 255); let index = 0; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { pixelData[index++] = (x + rnd) % 256; // RED pixelData[index++] = 0; // GREEN pixelData[index++] = 0; // BLUE pixelData[index++] = 255; // ALPHA } } canvasContext.putImageData(imageData, 0, 0); function getPixelData() { return pixelData; } function getImageData() { return imageData; } function getCanvas() { return canvas; } const image = { imageId: imageId, minPixelValue: 0, maxPixelValue: 255, slope: 1.0, intercept: 0, windowCenter: 128, windowWidth: 255, render: cornerstone.renderColorImage, getPixelData: getPixelData, getImageData: getImageData, getCanvas: getCanvas, rows: height, columns: width, height: height, width: width, color: true, columnPixelSpacing: 1.0, rowPixelSpacing: 1.0, invert: false, sizeInBytes : width * height * 4 }; return { promise: new Promise((resolve) => resolve(image)), cancelFn: undefined }; } cornerstone.registerImageLoader('colorImageLoader', loadImage); // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); function onImageRendered(e) { const eventData = e.detail; document.getElementById('renderTime').textContent = "Render Time:" + eventData.renderTimeInMs + " ms"; } element.addEventListener('cornerstoneimagerendered', onImageRendered); // load image and display it const imageId = "colorImageLoader://1"; cornerstone.loadImage(imageId).then(function(image) { cornerstone.displayImage(element, image); }); // add event handlers to mouse move to adjust window/center element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } function mouseUpHandler() { document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); </script>
Image Cache
This example shows how the image cache works
这个例子展示了图像缓存的工作原理
<script> const cacheInfo = cornerstone.imageCache.getCacheInfo(); document.getElementById('maxCacheSize').value = cacheInfo.maximumSizeInBytes; document.getElementById('apply').addEventListener('click', function () { const maxSizeInBytes = parseFloat(document.getElementById('maxCacheSize').value); cornerstone.imageCache.setMaximumSizeBytes(maxSizeInBytes); }); document.getElementById('purge').addEventListener('click', function () { cornerstone.imageCache.purgeCache(); document.getElementById('currentCacheSize').value = cacheInfo.cacheSizeInBytes; document.getElementById('numImagesCached').value = cacheInfo.numberOfImagesCached; }); let imageNum = 2; document.getElementById('addImage').addEventListener('click', function() { const imageId = "colorImageLoader://" + imageNum++; cornerstone.loadAndCacheImage(imageId).then(function(image) { const cacheInfo = cornerstone.imageCache.getCacheInfo(); cornerstone.displayImage(element, image); document.getElementById('currentCacheSize').value = cacheInfo.cacheSizeInBytes; document.getElementById('numImagesCached').value = cacheInfo.numberOfImagesCached; }); }); document.getElementById('changeImageSize').addEventListener('click', function() { const imageId = "colorImageLoader://1"; cornerstone.imageCache.changeImageIdCacheSize(imageId, 512 * 1024); }); var canvas = document.createElement('canvas'); // Loads an image given an imageId function loadImage (imageId) { var width = 256; var height = 256; canvas.width = width; canvas.height = height; var canvasContext = canvas.getContext('2d'); var imageData = canvasContext.createImageData(width, height); var pixelData = imageData.data; var index = 0; var rnd = Math.round(Math.random() * 255); for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { pixelData[index++] = (x + rnd) % 256; // RED pixelData[index++] = 0; // GREEN pixelData[index++] = 0; // BLUE pixelData[index++] = 255; // ALPHA } } canvasContext.putImageData(imageData, 0, 0); function getPixelData() { return pixelData; } function getImageData() { return imageData; } function getCanvas() { return canvas; } var image = { imageId: imageId, minPixelValue: 0, maxPixelValue: 255, slope: 1.0, intercept: 0, windowCenter: 127, windowWidth: 256, getPixelData: getPixelData, getImageData: getImageData, getCanvas: getCanvas, render : cornerstone.renderColorImage, rows: height, columns: width, height: height, width: width, color: true, columnPixelSpacing: 1.0, rowPixelSpacing: 1.0, invert: false, sizeInBytes : width * height * 4 }; return { promise: new Promise((resolve) => resolve(image)), cancelFn: undefined } } cornerstone.registerImageLoader('colorImageLoader', loadImage); // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); function onImageRendered(e) { const eventData = e.detail; document.getElementById('renderTime').textContent = "Render Time:" + eventData.renderTimeInMs + " ms"; } element.addEventListener('cornerstoneimagerendered', onImageRendered); // load image and display it const imageId = "colorImageLoader://1"; cornerstone.loadAndCacheImage(imageId).then(function(image) { cornerstone.displayImage(element, image); // add event handlers to mouse move to adjust window/center element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } function mouseUpHandler() { document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); }); </script>
Dynamic Image
This example shows how to integrate dynamically generated images with cornerstone. A dynamic image generator is one that can produce new images on the client side. This could be used to do image fusion as well as MPR or Volume Rendering.
这个例子展示了如何将动态生成的图像与基石集成。动态图像生成器可以在客户端生成新图像。这可以用于图像融合,以及MPR或体绘制。
<script> function getPixelData() { const width = 256; const height = 256; const numPixels = width * height; const pixelData = new Uint16Array(numPixels); let index = 0; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { pixelData[index] = ((x) % 256) * this.data.opacity; index++; } } return pixelData; } const dynamicImage = { imageId: "notneeded", minPixelValue: 0, maxPixelValue: 255, slope: 1.0, intercept: 0, windowCenter: 127, windowWidth: 256, render: cornerstone.renderGrayscaleImage, getPixelData: getPixelData, rows: 256, columns: 256, height: 256, width: 256, color: false, columnPixelSpacing: 1.0, rowPixelSpacing: 1.0, invert: false, sizeInBytes: 256 * 256 * 2, data: { opacity: 0.5 } }; // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); cornerstone.displayImage(element, dynamicImage); document.getElementById('opacity25').addEventListener('click', function () { dynamicImage.data.opacity = .25; cornerstone.updateImage(element, true); }); document.getElementById('opacity75').addEventListener('click', function () { dynamicImage.data.opacity = .75; cornerstone.updateImage(element, true); }); </script>
Flip and Rotate
This is an example of image flips and rotations
In this example,the image can be flipped (Horizontal/Vertical) or rotated (Clockwise/Anti-clockwise)
这是一个图像翻转和旋转的示例
在本例中,图像可以翻转(水平/垂直)或旋转(顺时针/逆时针)
<!-- include special code for these examples which provides images --> <script src="../exampleImageIdLoader.js"></script> <script> // image enable the dicomImage element const element = document.getElementById('dicomImage'); cornerstone.enable(element); //load the image and display it const imageId = 'example://1'; cornerstone.loadImage(imageId).then(function(image) { cornerstone.displayImage(element, image); }); // Add event handlers to flip or rotate the image document.getElementById('hFlip').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.hflip = !viewport.hflip; cornerstone.setViewport(element, viewport); }); document.getElementById('vFlip').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.vflip = !viewport.vflip; cornerstone.setViewport(element, viewport); }); document.getElementById('lRotate').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.rotation-=90; cornerstone.setViewport(element, viewport); }); document.getElementById('rRotate').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.rotation+=90; cornerstone.setViewport(element, viewport); }); document.getElementById('reset').addEventListener('click', function (e) { cornerstone.reset(element); }); element.addEventListener('mousemove', function(event) { const pixelCoords = cornerstone.pageToPixel(element, event.pageX, event.pageY); document.getElementById('coords').textContent = "pageX=" + event.pageX + ", pageY=" + event.pageY + ", pixelX=" + pixelCoords.x + ", pixelY=" + pixelCoords.y; }); </script>
Modality LUT and VOI LUT
This example shows the application of Modality LUT and VOI LUT to the image display pipeline
这个例子展示了模态LUT和VOI LUT在图像显示管道中的应用
<script> // create an inverting lut var modalityLUT = { id : '1', firstValueMapped: 0, numBitsPerEntry : 8, lut: [] }; for(let i=0; i < 256; i++) { modalityLUT.lut[i] = 255 - i; } var voiLUT = { id : '1', firstValueMapped: 0, numBitsPerEntry : 8, lut: [] }; for(let i=0; i < 256; i++) { voiLUT.lut[i] = i / 2 + 127; } //create VOI Presets var presetVoiLUT = { id: '2', firstValueMapped: 0, numBitsPerEntry: 8, lut: [] }; for (let i = 0; i < 256; i++) { presetVoiLUT.lut[i] = Math.floor(Math.random() * 256); } var voiPresets = [{ ww: 200, wc: 50 }, { ww: 100, wc: 127 }, { voiLUT: presetVoiLUT }]; // Loads an image given an imageId function loadImage(imageId) { var width = 256; var height = 256; var numPixels = width * height; var pixelData = new Uint16Array(numPixels); var index = 0; var rnd = 0;// Math.round(Math.random() * 255); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { pixelData[index] = (x + rnd) % 256; index++; } } function getPixelData() { return pixelData; } var image = { imageId: imageId, minPixelValue: 0, maxPixelValue: 255, slope: 1.0, intercept: 0, windowCenter: 127, windowWidth: 256, render: cornerstone.renderGrayscaleImage, getPixelData: getPixelData, rows: height, columns: width, height: height, width: width, color: false, columnPixelSpacing: 1.0, rowPixelSpacing: 1.0, invert: false, sizeInBytes: width * height * 2 }; return { promise: new Promise((resolve) => resolve(image)), cancelFn: undefined }; } cornerstone.registerImageLoader('myImageLoader', loadImage); // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); // load the image and display it const imageId = 'myImageLoader://1'; cornerstone.loadImage(imageId).then(function(image) { cornerstone.displayImage(element, image); let viewport = cornerstone.getViewport(element); viewport.voi.voiPresets = voiPresets; cornerstone.setViewport(element, viewport); }); document.getElementById('toggleModalityLUT').addEventListener('click', function() { const applyModalityLUT = document.getElementById('toggleModalityLUT').checked; console.log('applyModalityLUT=', applyModalityLUT); let viewport = cornerstone.getViewport(element); if(applyModalityLUT) { viewport.modalityLUT = modalityLUT; } else { viewport.modalityLUT = undefined; } cornerstone.setViewport(element, viewport); }); document.getElementById('toggleVOILUT').addEventListener('click', function() { const applyVOILUT = document.getElementById('toggleVOILUT').checked; console.log('applyVOILUT=', applyVOILUT); let viewport = cornerstone.getViewport(element); if(applyVOILUT) { viewport.voiLUT = voiLUT; } else { viewport.voiLUT = undefined; } document.getElementById('selectPreset').value = -1; cornerstone.setViewport(element, viewport); }); document.getElementById('selectPreset').addEventListener("change", function () { let selectedIndex = parseInt(document.getElementById('selectPreset').value, 10); let image = cornerstone.getImage(element); let viewport = cornerstone.getViewport(element); if (selectedIndex >= 0 && selectedIndex < voiPresets.length) { let voiPreset = voiPresets[selectedIndex]; //keep old values as a state since user might only pass the voiLUT viewport.voi.windowWidth = (voiPreset.ww === undefined) ? viewport.voi.windowWidth : voiPreset.ww; viewport.voi.windowCenter = (voiPreset.wc === undefined) ? viewport.voi.windowCenter : voiPreset.wc; //this always apply viewport.voiLUT = voiPreset.voiLUT; cornerstone.setViewport(element, viewport); document.getElementById('toggleVOILUT').checked = false; } else { resetVoiLUT(); } }); document.getElementById('resetVOI').addEventListener("click", function () { resetVoiLUT(); }); function resetVoiLUT() { let viewport = cornerstone.getViewport(element); document.getElementById('selectPreset').value = -1; document.getElementById('toggleVOILUT').checked = false; viewport.voiLUT = undefined; viewport.voi.windowWidth = undefined; viewport.voi.windowCenter = undefined; cornerstone.setViewport(element, viewport); } </script>
All features (scroll, zoom, pan, window/level, html overlays, resize, invert, interpolation)
This is an example of interactive series scroll, pan, zoom and window/level with HTML based overlays.
Controls:
- Left click drag - window/level
- Middle Mouse button drag - pan
- Right click drag - zoom
- Mouse wheel - scroll images
这是一个交互式系列滚动、平移、缩放和基于HTML的覆盖的窗口/级别的示例。
控制:
左键单击拖动-窗口/标高
鼠标中键拖动-平移
右键单击拖动-缩放
鼠标滚轮-滚动图像
<script> const imageIds = [ 'example://1', 'example://2' ]; let currentImageIndex = 0; // updates the image display function updateTheImage(imageIndex) { return cornerstone.loadAndCacheImage(imageIds[imageIndex]).then(function(image) { currentImageIndex = imageIndex; const viewport = cornerstone.getViewport(element); cornerstone.displayImage(element, image, viewport); }); } // image enable the element const element = document.getElementById('dicomImage'); cornerstone.enable(element); // setup handlers before we display the image function onImageRendered(e) { const eventData = e.detail; // set the canvas context to the image coordinate system cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, eventData.canvasContext); // NOTE: The coordinate system of the canvas is in image pixel space. Drawing // to location 0,0 will be the top left of the image and rows,columns is the bottom // right. const context = eventData.canvasContext; context.beginPath(); context.strokeStyle = 'white'; context.lineWidth = .5; context.rect(128, 90, 50, 60); context.stroke(); context.fillStyle = "white"; context.font = "6px Arial"; context.fillText("Tumor Here", 128, 85); document.getElementById('topright').textContent = "Render Time:" + eventData.renderTimeInMs + " ms"; document.getElementById('bottomleft').textContent = "WW/WL:" + Math.round(eventData.viewport.voi.windowWidth) + "/" + Math.round(eventData.viewport.voi.windowCenter); document.getElementById('bottomright').textContent = "Zoom:" + eventData.viewport.scale.toFixed(2); } element.addEventListener('cornerstoneimagerendered', onImageRendered); // load and display the image const imagePromise = updateTheImage(0); // add handlers for mouse events once the image is loaded. imagePromise.then(function() { // add event handlers to pan image on mouse move element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; const mouseButton = e.which; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; if (mouseButton === 1) { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 2) { let viewport = cornerstone.getViewport(element); viewport.translation.x += (deltaX / viewport.scale); viewport.translation.y += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 3) { let viewport = cornerstone.getViewport(element); viewport.scale += (deltaY / 100); cornerstone.setViewport(element, viewport); } } function mouseUpHandler() { document.removeEventListener('mouseup', mouseUpHandler); document.removeEventListener('mousemove', mouseMoveHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); const mouseWheelEvents = ['mousewheel', 'DOMMouseScroll']; mouseWheelEvents.forEach(function(eventType) { element.addEventListener(eventType, function (e) { // Firefox e.detail > 0 scroll back, < 0 scroll forward // chrome/safari e.wheelDelta < 0 scroll back, > 0 scroll forward if (e.wheelDelta < 0 || e.detail > 0) { if (currentImageIndex === 0) { updateTheImage(1); } } else { if (currentImageIndex === 1) { updateTheImage(0); } } // Prevent page from scrolling return false; }); }); // Add event handler to the ww/wc apply button document.getElementById('x256').addEventListener('click', function (e) { element.style.width = '256px'; element.style.height = '256px'; cornerstone.resize(element); }); document.getElementById('x512').addEventListener('click', function (e) { element.style.width = '512px'; element.style.height = '512px'; cornerstone.resize(element); }); document.getElementById('invert').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.invert = !viewport.invert; cornerstone.setViewport(element, viewport); }); document.getElementById('interpolation').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.pixelReplication = !viewport.pixelReplication; cornerstone.setViewport(element, viewport); }); document.getElementById('hflip').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.hflip = !viewport.hflip; cornerstone.setViewport(element, viewport); }); document.getElementById('vflip').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.vflip = !viewport.vflip; cornerstone.setViewport(element, viewport); }); document.getElementById('rotate').addEventListener('click', function (e) { const viewport = cornerstone.getViewport(element); viewport.rotation += 90; cornerstone.setViewport(element, viewport); }); element.addEventListener('mousemove', function(event) { const pixelCoords = cornerstone.pageToPixel(element, event.pageX, event.pageY); document.getElementById('coords').textContent = "pageX=" + event.pageX + ", pageY=" + event.pageY + ", pixelX=" + pixelCoords.x + ", pixelY=" + pixelCoords.y; }); }); </script>
Using WebGL Rendering option
This is an example of WebGL use in cornerstone
这是一个WebGL在基石中的使用示例
<!-- include special code for these examples which provides images --> <script src="../exampleImageIdLoader.js"></script> <script> const element = document.getElementById('dicomImage'); const elementWebGL = document.getElementById('dicomImageWebGL'); // setup handlers before we display the image function onImageRendered(e) { const eventData = e.detail; // set the canvas context to the image coordinate system cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, eventData.canvasContext); const parent = eventData.element.parentNode; parent.querySelector('.renderTime').textContent = "Render Time:" + eventData.renderTimeInMs + " ms"; parent.querySelector('.wwwc').textContent = "WW/WL:" + Math.round(eventData.viewport.voi.windowWidth) + "/" + Math.round(eventData.viewport.voi.windowCenter); } element.addEventListener('cornerstoneimagerendered', onImageRendered); elementWebGL.addEventListener('cornerstoneimagerendered', onImageRendered); const imageId = 'example://1'; cornerstone.enable(element); cornerstone.loadAndCacheImage(imageId).then(function(image) { cornerstone.displayImage(element, image); }); const options = { renderer: 'webgl' }; cornerstone.enable(elementWebGL, options); cornerstone.loadAndCacheImage(imageId).then(function(image) { cornerstone.displayImage(elementWebGL, image); }); const elements = [element, elementWebGL]; elements.forEach(function(elem) { // add event handlers to mouse move to adjust window/center elem.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; let viewport = cornerstone.getViewport(elem); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(elem, viewport); } function mouseUpHandler() { document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); }); </script>
False Color Mapping
This example shows you how to use the pre-defined colormaps or create a custom lookup table and use them to create a false color mapping
本示例说明如何使用预定义的颜色图或创建自定义查找表,并使用它们创建错误的颜色映射。
<script> // Populate colormap dropdown with all the default ones available // in cornerstone and also a "Custom" option function fillColormapsList() { const dropdown = document.getElementById('colormaps'); const colormapsList = cornerstone.colors.getColormapsList(); const addOption = function(id, name, disabled) { const option = document.createElement("OPTION"); option.value = id; option.textContent = name; option.disabled = !!disabled; dropdown.append(option); }; colormapsList.forEach(function(colormapItem) { addOption(colormapItem.id, colormapItem.name); }); // Horizontal Line addOption('', '──────────', true); addOption('custom', 'Custom'); } // image enable the dicomImage element const element = document.getElementById('dicomImage'); cornerstone.enable(element); const imageId = 'example://1'; // Load the example image and display it cornerstone.loadImage(imageId).then(function(image) { cornerstone.displayImage(element, image); // add event handlers to pan image on mouse move element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; const mouseButton = e.which; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; if (mouseButton === 1) { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 2) { let viewport = cornerstone.getViewport(element); viewport.translation.x += (deltaX / viewport.scale); viewport.translation.y += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 3) { let viewport = cornerstone.getViewport(element); viewport.scale += (deltaY / 100); cornerstone.setViewport(element, viewport); } } function mouseUpHandler() { document.removeEventListener('mouseup', mouseUpHandler); document.removeEventListener('mousemove', mouseMoveHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); }); // Dropdown listener to get the new colormap // selected by the user and update the image function colormapChanged() { const viewport = cornerstone.getViewport(element); const colormapId = document.getElementById('colormaps').value; let colormap; // Use selected the first option ("Select...") if (colormapId === '') { return; } else if(colormapId === 'custom') { colormap = getCustomLookupTable(); } else { colormap = cornerstone.colors.getColormap(colormapId); } viewport.colormap = colormap; cornerstone.setViewport(element, viewport); cornerstone.updateImage(element, true); // Update the colorbar at the top of the image updateColorbar(colormap); } function getCustomLookupTable(minPixelValue, maxPixelValue) { const colormap = cornerstone.colors.getColormap('myCustomColorMap'); colormap.setNumberOfColors(6); // You can also use `addColor` but in this case it wouldn't work. // Any colormap returned by `getColormap` lasts forever (global) and // calling `addColor` would result in duplicated colors. colormap.insertColor(0, [188, 252, 201, 255]); // Banana colormap.insertColor(1, [245, 222, 179, 255]); // Wheat colormap.insertColor(2, [255, 125, 64, 255]); // Flesh colormap.insertColor(3, [135, 38, 87, 255]); // Raspberry colormap.insertColor(4, [227, 206, 87, 255]); // Mint colormap.insertColor(5, [ 51, 160, 201, 255]); // Peacock return colormap; } // Update the colorbar at the top of the image function updateColorbar(colormap) { const lookupTable = colormap.createLookupTable(); const canvas = document.getElementById('colorbar'); const ctx = canvas.getContext('2d'); const height = canvas.height; const width = canvas.width; const colorbar = ctx.createImageData(512, 20); // Set the min and max values then the lookup table // will be able to return the right color for this range lookupTable.setTableRange(0, width); // Update the colorbar pixel by pixel for(let col = 0; col < width; col++) { const color = lookupTable.mapValue(col); for(let row = 0; row < height; row++) { const pixel = (col + row * width) * 4; colorbar.data[pixel] = color[0]; colorbar.data[pixel+1] = color[1]; colorbar.data[pixel+2] = color[2]; colorbar.data[pixel+3] = color[3]; } } ctx.putImageData(colorbar, 0, 0); } document.getElementById('colormaps').addEventListener('change', colormapChanged); fillColormapsList(); </script>
Composite Images
This example shows you how to add and interact with layers.
此示例向您展示了如何添加层并与层交互。
视口值:
视口比例:
层CT值:
视口比例:
图层视口比例:
层同步比例:
层宠物值:
图层视口比例:
层同步比例:
<!-- include special code for these examples which provides images --> <script src="../petctImageIdLoader.js"></script> <script src="../petctMetaDataProvider.js"></script> <script> // Enable the dicomImage element const element = document.getElementById('dicomImage'); cornerstone.enable(element); // JSON with all layers to be loaded // The `name` option is used only by this example and // cornerstone doesn't even know that it exists. // You can add any option you want to `options` object. const layers = [{ imageId: 'ct://1', options: { name: 'CT' } }, { imageId: 'pet://1', options: { name: 'PET', opacity: 0.7, viewport: { colormap: 'hotIron', voi: { windowWidth: 30, windowCenter: 16 } } } } ]; // This is the main function responsible for loading all layers // This method will wait for all images to be loaded (`loadImages`) // before adding the layers function loadLayers() { loadImages().then(function(images) { images.forEach(function(image, index) { const layer = layers[index]; const layerId = cornerstone.addLayer(element, image, layer.options); cornerstone.updateImage(element); console.log('Layer ' + index + ': ' + layerId); }); // Update dropdown size to make all layers name visible const layersDropdown = document.getElementById('layers'); layersDropdown.size = layers.length; // Listen to `change` event to set the selected layer as active layersDropdown.addEventListener('change', function(event) { const layerId = event.currentTarget.value; cornerstone.setActiveLayer(element, layerId); }); }); } // This method loads the image of each layer and resolve the // promise only after getting all of them loaded function loadImages() { const promises = []; layers.forEach(function(layer) { const loadPromise = cornerstone.loadAndCacheImage(layer.imageId); promises.push(loadPromise); }); return Promise.all(promises); } // Select the right layer in the dropdown function updateSelectedLayer(layerId) { const layers = document.getElementById('layers'); const currentLayerId = layers.value; if(currentLayerId !== layerId) { layers.value = layerId; // Trigger a change event const event = new Event('change'); element.dispatchEvent(event); } } // Listen to `change` event to activate/deactivate the viewport synchronization document.querySelector('input[name=syncViewports]').addEventListener('change', function(event) { const enabledElement = cornerstone.getEnabledElement(element); enabledElement.syncViewports = event.currentTarget.checked; cornerstone.updateImage(element); }); document.getElementById('colormaps').addEventListener('change', function() { const layer = cornerstone.getActiveLayer(element); layer.viewport.colormap = document.getElementById('colormaps').value; cornerstone.updateImage(element); }); // Listen to `change` event to update the opacity of the active layer document.getElementById("imageOpacity").addEventListener('change', function(event) { const layer = cornerstone.getActiveLayer(element); layer.options.opacity = parseFloat(event.currentTarget.value); cornerstone.updateImage(element); }); // Listen to `change` event to update the visibility of the active layer document.querySelector('input[name=visible]').addEventListener('change', function(event) { const layer = cornerstone.getActiveLayer(element); layer.options.visible = event.currentTarget.checked; cornerstone.updateImage(element); }); // This event will be called every time a layer is added through cornerstone.addLayer // The layer is added to the dropdown to make it possible to select and interact with it element.addEventListener('cornerstonelayeradded', function(e) { const eventData = e.detail; const layer = cornerstone.getLayer(eventData.element, eventData.layerId); const layers = document.getElementById('layers'); const layerOption = document.createElement("OPTION"); layerOption.value = layer.layerId; layerOption.textContent = layer.options.name; // Set the layer as selected in case its the the first layer to be added if(layers.childElementCount === 0) { layerOption.checked = true; } layers.appendChild(layerOption); }); // This event will be called every time cornerstone.setActiveLayer is called // We need to load the layer properties and update the selected layer in the dropdown element.addEventListener('cornerstoneactivelayerchanged', function(e) { const eventData = e.detail; const layer = cornerstone.getActiveLayer(element); const colormap = layer.viewport.colormap || ''; const opacity = layer.options.opacity == null ? 1 : layer.options.opacity; // Restore all properties for the active layer document.getElementById('imageOpacity').value = opacity; document.querySelector("input[name=visible]").checked = layer.options.visible === undefined ? true : layer.options.visible; document.getElementById('colormaps').value = colormap; updateSelectedLayer(eventData.layerId); }); // Populate colormap dropdown with all the default ones function fillColormapsList() { const dropdown = document.getElementById('colormaps'); const colormapsList = cornerstone.colors.getColormapsList(); const addOption = function(id, name) { const option = document.createElement("OPTION"); option.value = id; option.textContent = name; dropdown.append(option); }; colormapsList.forEach(function(colormapItem) { addOption(colormapItem.id, colormapItem.name); }); } // add event handlers to pan image on mouse move element.addEventListener('mousedown', function (e) { let lastX = e.pageX; let lastY = e.pageY; const mouseButton = e.which; function mouseMoveHandler(e) { const deltaX = e.pageX - lastX; const deltaY = e.pageY - lastY; lastX = e.pageX; lastY = e.pageY; if (mouseButton === 1) { let viewport = cornerstone.getViewport(element); viewport.voi.windowWidth += (deltaX / viewport.scale); viewport.voi.windowCenter += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 2) { let viewport = cornerstone.getViewport(element); viewport.translation.x += (deltaX / viewport.scale); viewport.translation.y += (deltaY / viewport.scale); cornerstone.setViewport(element, viewport); } else if (mouseButton === 3) { let viewport = cornerstone.getViewport(element); viewport.scale += (deltaY / 100); cornerstone.setViewport(element, viewport); document.getElementById('layerViewportScale').innerText = viewport.scale; } const layer1 = cornerstone.getLayers(element)[0]; const layer2 = cornerstone.getLayers(element)[1]; document.getElementById('layer1LayerViewportScale').innerText = layer1.viewport.scale; document.getElementById('layer2LayerViewportScale').innerText = layer2.viewport.scale; document.getElementById('layer1LayerSyncScale').innerText = layer1.syncProps.originalScale; document.getElementById('layer2LayerSyncScale').innerText = layer2.syncProps.originalScale; } function mouseUpHandler() { document.removeEventListener('mouseup', mouseUpHandler); document.removeEventListener('mousemove', mouseMoveHandler); } document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }); const mouseWheelEvents = ['mousewheel', 'DOMMouseScroll']; mouseWheelEvents.forEach(function(eventType) { element.addEventListener(eventType, function (e) { // Firefox e.detail > 0 scroll back, < 0 scroll forward // chrome/safari e.wheelDelta < 0 scroll back, > 0 scroll forward let viewport = cornerstone.getViewport(element); if (e.wheelDelta < 0 || e.detail > 0) { viewport.scale -= 0.25; } else { viewport.scale += 0.25; } cornerstone.setViewport(element, viewport); // Prevent page from scrolling return false; }); }); // Start point to load all layers and colormaps and // also attach a click listener to each tool loadLayers(); fillColormapsList(); </script>
DICOM Displayed Area Module support
This example demonstrates the IHE Image Display Test 521: Consistent Presentation of Images.
这个例子演示了IHE图像显示测试521:图像的一致表示。
<!-- include special code for these examples which provides images --> <script src="../exampleImageIdLoaderRaw.js"></script> <script> const viewportOptions = { scale: 1.0, translation: { x: 0, y: 0 }, invert: false, pixelReplication: false }; const element = document.getElementById('dicomImage'); const origElement = document.getElementById('origDicomImage'); loadSelectedCase(); document.getElementById('testCasesSelect').addEventListener('change', () => { loadSelectedCase(); }); document.getElementById('area-apply').addEventListener("click", function () { let viewport = cornerstone.getViewport(element); viewport.displayedArea.tlhc.x = document.getElementById('area-left').value; viewport.displayedArea.tlhc.y = document.getElementById('area-top').value; viewport.displayedArea.brhc.x = document.getElementById('area-right').value; viewport.displayedArea.brhc.y = document.getElementById('area-bottom').value; viewport.displayedArea.rowPixelSpacing = document.getElementById('row-pixel-spacing').value; viewport.displayedArea.columnPixelSpacing = document.getElementById('col-pixel-spacing').value; viewport.displayedArea.presentationSizeMode = document.getElementById('presentation-mode').value; document.getElementById('test-result').textContent = ""; document.getElementById('test-image').textContent = ""; document.getElementById('test-pState').textContent = ""; cornerstone.setViewport(element, viewport); }); function loadSelectedCase() { let imageId = document.getElementById('testCasesSelect').value; cornerstone.disable(element); cornerstone.disable(origElement); cornerstone.enable(element); cornerstone.enable(origElement); cornerstone.loadImage(imageId).then(function (image) { cornerstone.displayImage(element, image, viewportOptions); cornerstone.displayImage(origElement, image, viewportOptions); cornerstone.fitToWindow(origElement); applyTestViewport(element,image); }); } function applyTestViewport(element, image) { if (image.testCase) { let viewport = cornerstone.getViewport(element); viewport.displayedArea = image.testCase.displayedArea; cornerstone.setViewport(element, viewport); updateUI(element, image, viewport, image.testCase); } } function updateUI(element, image, viewport, testCase) { if (testCase) { document.getElementById('test-image').textContent = testCase.image; document.getElementById('test-pState').textContent = testCase.pState; document.getElementById('test-result').textContent = testCase.result; } document.getElementById('area-left').value = viewport.displayedArea.tlhc.x; document.getElementById('area-top').value = viewport.displayedArea.tlhc.y; document.getElementById('area-right').value = viewport.displayedArea.brhc.x; document.getElementById('area-bottom').value = viewport.displayedArea.brhc.y; document.getElementById('row-pixel-spacing').value = viewport.displayedArea.rowPixelSpacing; document.getElementById('col-pixel-spacing').value = viewport.displayedArea.columnPixelSpacing; document.getElementById('viewerSize').textContent = element.clientWidth + 'px X ' + element.clientHeight + 'px'; document.getElementById('imageSize').textContent = image.width + 'px X ' + image.height + 'px'; document.getElementById('presentation-mode').value = viewport.displayedArea.presentationSizeMode; } </script>