// Dicom saved settings var dicom_settings_db = new Dexie("dicom_settings"); dicom_settings_db.version(1).stores({ mouse_bindings: "button,mode,tool", }); cornerstoneBase64ImageLoader.external.cornerstone = cornerstone; cornerstoneWebImageLoader.external.cornerstone = cornerstone; cornerstoneWADOImageLoader.external.cornerstone = cornerstone; cornerstoneTools.init(); let tool_state = {}; export function loadCornerstone(main_element, db, images, annotations_to_load, load_as_stack = false) { // canvas-panel holds the enabled elements and all tools / menus main_element.append("
"); main_element[0].dataset.collapsed = "none"; $(".canvas-panel").append($("
")); //$(".canvas-panel").append($("
").load("dicomSummary.html")); let single_dicom_viewer = main_element.find(".single-dicom-viewer").get(0); console.log(single_dicom_viewer); $(".canvas-panel").append($(`
close
`)); // Add generic settings menus to canvas-panel $(".canvas-panel").append( $(`
close view log

Image viewer settings:

Primary

Secondary (Ctrl)
`) ) .append($("")) .append($("")) .append($("")) .append($("")); $(single_dicom_viewer).append( $( "
Image of
wc: ww:
" ) ) // Add buttons .append( $(`
`) ); let window_presets = { "window-btn-abdomen": [400, 50], "window-btn-liver": [150, 30], "window-btn-mediastinum": [350, 50], "window-btn-lung": [1500, -600], "window-btn-brain": [80, 40], "window-btn-stroke": [40, 40], "window-btn-bone": [1800, 400] } $("#dicom-window-panel button").each((n, el) => { $(el).click((e) => { console.log(e); console.log(e.target); let button_id = e.target.id; let [ww, wc] = window_presets[button_id]; let viewport = cornerstone.getViewport(single_dicom_viewer); viewport.voi.windowWidth = parseFloat(ww); viewport.voi.windowCenter = parseFloat(wc); cornerstone.setViewport(single_dicom_viewer, viewport); e.preventDefault(); }) }) $("#dicom-settings-close").click(e => { $("#dicom-settings-panel").hide(); e.preventDefault(); }); $("#dicom-log-close").click(e => { $("#dicom-log-panel").hide(); e.preventDefault(); }); $(".view-log-button").click(e => { $("#dicom-settings-panel").hide(); $("#dicom-log-panel").show(); e.preventDefault(); }); $("#dicom-settings-button").click(e => { $("#dicom-settings-panel").toggle(); }); $("#dicom-fullscreen-button").click(e => { if (!document.fullscreenElement) { $(".canvas-panel").get(0).requestFullscreen().catch(err => { alert(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`); }); } else { document.exitFullscreen(); } }); $("#dicom-toggle-mode-button").click(e => { //$("#dicom-settings-panel").toggle(); let stack = !load_as_stack; $(main_element).empty(); loadCornerstone(main_element, db, images, annotations_to_load, stack) }); $("#dicom-collapse-button").click(e => { //$("#dicom-settings-panel").toggle(); //let stack = !load_as_stack; if (main_element[0].dataset.collapsed == "none") { main_element[0].dataset.collapsed = main_element.width(); main_element.width("50px"); } else { main_element.width(main_element[0].dataset.collapsed); main_element[0].dataset.collapsed = "none" } resizeHandler(); //loadCornerstone(main_element, db, images, annotations_to_load, stack) }); //let images = data["images"]; // Make sure we have an array if (!Array.isArray(images)) { images = [images]; } console.log("Images", images) //let annotations = []; //if (annotations_to_load) { //annotations = annotations_to_load[figure_to_load.split("-")[1]]; //if (!Array.isArray(annotations)) { //annotations = [annotations]; //} //} let annotations = annotations_to_load; if (!Array.isArray(annotations)) { annotations = [annotations]; } console.log("annon", annotations); function loadAnnotation(imageId, annotation) { //console.log("loadAnnotations", imageId, annotation); const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager; if (annotation == undefined || annotation.length < 1 || annotation == [undefined]) { return } let tool_state_no_id = JSON.parse(annotation); tool_state[imageId] = tool_state_no_id; toolStateManager.restoreToolState(tool_state); } async function load(images, annotations) { console.log("LOAD", images); let imageIds = []; for (let i = 0; i < images.length; i++) { let data_url = images[i]; const annotation = annotations[i]; // check stack type if (data_url.startsWith("data:image")) { let imageId = "base64://" + data_url.split(",")[1]; loadAnnotation(imageId, annotation); imageIds.push(imageId); // Treat application/octet-stream as if they are dicoms } else if (data_url.startsWith("data:application/dicom") || data_url.startsWith("data:application/octet-stream")) { //stack = stack.split(";")[1]; let dfile = await urltoFile(data_url, "dicom", "application/dicom"); const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add( dfile ); loadAnnotation(imageId, annotation); imageIds.push(imageId); //cornerstone.loadImage(imageId).then(function(image) { // tempFunction(image); //}); } else { let url; // This doesn't seem to have any benefit //if (data_url.startsWith("http")) { // url = data_url; //} else { // url = window.location.href.replace(/\/\#\/?$/, '') + "/" + data_url //} url = data_url; if (url.endsWith("dcm")) { url = "wadouri:" + url; } // if there is no extension treat it as a dicom if (/(?:\/|^)[^.\/]+$/.test(url)) { url = "wadouri:" + url; } loadAnnotation(url, annotation); imageIds.push(url); } } const stack = { currentImageIdIndex: 0, imageIds }; //cornerstone.loadAndCacheImage(imageIds[0]).then(function(image) { //console.log("load and cache", imageIds[0]) cornerstone.loadAndCacheImage(imageIds[0]).then(function (image) { //console.log("LOAD and cache, then", image); console.log(oeu) loadCornerstoneMainImage(single_dicom_viewer, image, stack, db, load_as_stack); }) .catch((err, err2) => { console.log(err); $("#dicom-log-panel").append("
").append(err.stack); }); } if (images.length > 1 && load_as_stack == false) { $(".canvas-panel").append("
"); for (let id = 0; id < images.length; id++) { let n = id + 1; let thumb = $( "
" + n + "
" ); $("#image-thumbs").append(thumb); $("#thumb-" + id).click(selectThumbClick).mousedown(stopEvent); let image_url = images[id]; console.log(image_url) let img; if (image_url.startsWith("data")) { // based (image) data url, just load the image directly if (image_url.startsWith("data:image/")) { img = $("", { src: image_url, id: "thumb-image-" + id, class: "thumbnail", title: "Click on the thumbnail to view and manipulate the image.", draggable: "false", style: "height: 100px;" }); $("#thumb-" + id).append(img); // otherwise try to load it as a dicom } else { // convert the data url to a file urltoFile(image_url, "dicom", "application/dicom").then(function ( dfile ) { // load the file using cornerstoneWADO file loader const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add( dfile ); cornerstone.loadAndCacheImage(imageId).then(function (image) { img = $("
").get(0); img.id = "thumb-image-" + id; img.class = "thumbnail"; img.title = "Click on the thumbnail to view and manipulate the image."; img.draggable = "false"; img.style = "height: 100px; width: 100px"; $("#thumb-" + id).append(img); const element = document.getElementById("thumb-image-" + id); cornerstone.enable(element); cornerstone.displayImage(element, image); cornerstone.resize(element); }); //.catch( function(error) { }); } } else { if (!image_url.endsWith("dicom") && !image_url.endsWith("dcm")) { img = $("", { src: image_url, id: "thumb-image-" + id, class: "thumbnail", title: "Click on the thumbnail to view and manipulate the image.", draggable: "false", style: "height: 100px;" }); $("#thumb-" + id).append(img); // otherwise try to load it as a dicom } else { let url = "wadouri:" + image_url; console.log(url) cornerstone.loadAndCacheImage(url).then(function (image) { img = $("
").get(0); img.id = "thumb-image-" + id; img.class = "thumbnail"; img.title = "Click on the thumbnail to view and manipulate the image."; img.draggable = "false"; img.style = "height: 100px; width: 100px"; $("#thumb-" + id).append(img); const element = document.getElementById("thumb-image-" + id); cornerstone.enable(element); cornerstone.displayImage(element, image); cornerstone.resize(element); }); } } } } load(images, annotations); } function loadCornerstoneMainImage(element, image, stack, db, load_as_stack) { dumpDataSet(image.data) console.log("loadCornerstoneMainImage") console.log("load as stack:", load_as_stack) // It is probably silly to do this each time we load a question const PanTool = cornerstoneTools.PanTool; const ZoomTool = cornerstoneTools.ZoomTool; const ZoomMouseWheelTool = cornerstoneTools.ZoomMouseWheelTool; const StackScrollMouseWheelTool = cornerstoneTools.StackScrollMouseWheelTool; const WwwcTool = cornerstoneTools.WwwcTool; const WwwcRegionTool = cornerstoneTools.WwwcRegionTool; const RotateTool = cornerstoneTools.RotateTool; const StackScrollTool = cornerstoneTools.StackScrollTool; const MagnifyTool = cornerstoneTools.MagnifyTool; const ArrowAnnotateTool = cornerstoneTools.ArrowAnnotateTool; const RectangleRoiTool = cornerstoneTools.RectangleRoiTool; const LengthTool = cornerstoneTools.LengthTool; console.log("enable element", element); cornerstone.enable(element); cornerstone.displayImage(element, image); cornerstoneTools.addStackStateManager(element, ["stack"]); cornerstoneTools.addToolState(element, "stack", stack); cornerstoneTools.addToolForElement(element, PanTool); cornerstoneTools.addToolForElement(element, ZoomTool); cornerstoneTools.addToolForElement(element, ZoomMouseWheelTool); cornerstoneTools.addToolForElement(element, StackScrollMouseWheelTool); cornerstoneTools.addToolForElement(element, WwwcTool); cornerstoneTools.addToolForElement(element, WwwcRegionTool); cornerstoneTools.addToolForElement(element, RotateTool); cornerstoneTools.addToolForElement(element, StackScrollTool); cornerstoneTools.addToolForElement(element, MagnifyTool); cornerstoneTools.addToolForElement(element, LengthTool); //cornerstoneTools.addTool(ArrowAnnotateTool); //cornerstoneTools.addTool(RectangleRoiTool); cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { getTextCallback: () => { }, changeTextCallback: () => { }, allowEmptyLabel: true, renderDashed: false, drawHandles: false, drawHandlesOnHover: true, }, }); cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { showHounsfieldUnits: false, showMinMax: false, allowEmptyLabel: true, renderDashed: false, drawHandles: false, drawHandlesOnHover: true, }, }); cornerstoneTools.setToolEnabledForElement(element, "ArrowAnnotate"); let available_tools = [ "Pan", "Zoom", "Wwwc", "WwwcRegion", "Rotate", "StackScroll", "Magnify", "Length", "ArrowAnnotate", "RectangleRoi", ]; $(".mouse-binding-select option").remove(); available_tools.forEach(function (tool) { let option = ""; $(".mouse-binding-select").append(option); //$("#left-mouse-dicom").append(option); //$("#middle-mouse-dicom").append(option); //$("#right-mouse-dicom").append(option); }); $(".mouse-binding-select").on("change", function (e) { changeMouseBinding(e, db); }); // Set default tools $("#primary-mouse-binding .mouse-binding-select[data-button=1]").val("Pan"); $("#primary-mouse-binding .mouse-binding-select[data-button=2]").val("Wwwc"); $("#primary-mouse-binding .mouse-binding-select[data-button=4]").val("Zoom"); //cornerstoneTools.setToolActive("Pan", { mouseButtonMask: 1 }); console.log(load_as_stack); if (load_as_stack) { cornerstoneTools.setToolActiveForElement(element, "StackScrollMouseWheel", { mouseButtonMask: 3 }); } else { cornerstoneTools.setToolActiveForElement(element, "ZoomMouseWheel", { mouseButtonMask: 3 }); } //cornerstoneTools.setToolActive("Zoom", { mouseButtonMask: 4 }); //cornerstoneTools.setToolActive("Wwwc", { mouseButtonMask: 2 }); $("#secondary-mouse-binding .mouse-binding-select[data-button=1]").val( "Magnify" ); $("#secondary-mouse-binding .mouse-binding-select[data-button=2]").val( "Rotate" ); $("#secondary-mouse-binding .mouse-binding-select[data-button=4]").val( "WwwcRegion" ); loadPrimaryDicomInterface(db); loadAltDicomInterface(db); element.addEventListener("cornerstoneimagerendered", onImageRendered); //setDicomCanvasNonFullscreen(element); cornerstone.reset(element); //element.scrollIntoView(false); //element.scrollTo(0); $(element).dblclick(function () { cornerstone.reset(this); }); element.removeEventListener("wheel", element.wheelEventHandler); // Add tool selector //$(".canvas-panel").append( // '' //); //$(".control-overlay") // .get(0) // .addEventListener("change", changeControlSelection); resizeHandler(); window.addEventListener("resize", resizeHandler); element.addEventListener( "contextmenu", function (e) { // do something here... e.preventDefault(); }, false ); $(element).on("mouseup", function (event) { $(".magnifyTool").hide(); }); } function resizeHandler() { let element = document.getElementsByClassName("single-dicom-viewer")[0]; let h = window.innerHeight - $("#header").height() - 16; $(element).height(h); cornerstone.resize(element, true); } async function loadPrimaryDicomInterface(db) { const bindings = await dicom_settings_db.mouse_bindings.where({ mode: "0" }).toArray().catch((err) => { console.log(err); }); bindings.forEach(function (b) { let sel = $("#primary-mouse-binding select[data-button=" + b.button + "]").get( 0 ); sel.value = b.tool; //sel.dispatchEvent(new Event("change")); }); registerPrimaryDicomInterface(); } async function loadAltDicomInterface(db) { const bindings = await dicom_settings_db.mouse_bindings.where({ mode: "1" }).toArray().catch((err) => { console.log(err); }); bindings.forEach(function (b) { let sel = $( "#secondary-mouse-binding select[data-button=" + b.button + "]" ).get(0); sel.value = b.tool; }); } // Called when the dicom image is loaded / rendered function onImageRendered(e) { const eventData = e.detail; //console.log(e); // Update ww/wl $("#wc").text(Math.round(eventData.viewport.voi.windowCenter)); $("#ww").text(Math.round(eventData.viewport.voi.windowWidth)); // update stack data let stack = eventData.enabledElement.toolStateManager.toolState.stack.data[0]; $("#total_image_number").text(stack.imageIds.length); $("#current_image_number").text(parseInt(stack.currentImageIdIndex) + 1); if (stack.imageIds.length > 1) { $(".thumb").removeClass("thumb-active"); $("#thumb-" + stack.currentImageIdIndex).addClass("thumb-active"); } //if (stack.imageIds.length > 1) { // $("option[value=scroll").prop("disabled", false); // $("option[value=scroll").prop("hidden", false); // $("option[value=scroll").text( // "scroll (" + // (stack.currentImageIdIndex + 1) + // "/" + // stack.imageIds.length + // ")" // ); } function stopEvent(evt) { console.log("stop", evt) evt.preventDefault(); evt.stopPropagation(); } function selectThumbClick(evt) { let new_index = evt.currentTarget.dataset.id; selectThumb(new_index); evt.preventDefault(); } export function registerPrimaryDicomInterface() { // Set mousetools based upon the selected options let element = document.getElementsByClassName("single-dicom-viewer")[0]; let selections = $("#primary-mouse-binding select"); selections.each((i, option) => { cornerstoneTools.setToolActiveForElement(element, option.value, { mouseButtonMask: parseInt(option.dataset.button) }); }); } export function registerAltDicomInterface(e) { // Called when control is pressed // Set mousetools based upon the selected options let element = document.getElementsByClassName("single-dicom-viewer")[0]; let selections = $("#secondary-mouse-binding select"); selections.each((i, option) => { cornerstoneTools.setToolActiveForElement(element, option.value, { mouseButtonMask: parseInt(option.dataset.button) }); }); } function changeMouseBinding(e, db) { let select = e.currentTarget; let button = select.dataset.button; let mode = select.dataset.mode; let tool = select.value; // Directly activate primary tools (secondary will be activated when modifier // key is pressed if (mode == "0") { let element = document.getElementsByClassName("single-dicom-viewer")[0]; cornerstoneTools.setToolActiveForElement(element, tool, { mouseButtonMask: parseInt(button) }); } dicom_settings_db.mouse_bindings.put({ button: button, mode: mode, tool: tool }); //db.mouse_bindings.put({button: button, mode: mode, tool: tool}).then(loadPrimaryDicomInterface()); } export function selectThumb(new_index) { console.log("select thumb new index", new_index); // There must be a better way to do this... let dicom_element = document.getElementsByClassName("single-dicom-viewer")[0]; if (dicom_element == null) { return; } let c = cornerstone.getEnabledElement(dicom_element); // let current_index = // c.toolStateManager.toolState.stack.data[0].currentImageIdIndex; c.toolStateManager.toolState.stack.data[0].currentImageIdIndex = new_index; let id = c.toolStateManager.toolState.stack.data[0].imageIds[new_index]; console.log("select thumb id", id); console.log("select thumb el", dicom_element); cornerstone.loadImage(id).then(b => { console.log("b", b); cornerstone.displayImage(dicom_element, b); }); //c = cornerstone.getEnabledElement(dicom_element) } function urltoFile(url, filename, mimeType) { return fetch(url) .then(function (res) { return res.arrayBuffer(); }) .then(function (buf) { return new File([buf], filename, { type: mimeType }); }); } function dumpDataSet(dataSet) { $('span[data-dicom]').each(function(index, value) { var attr = $(value).attr('data-dicom'); var element = dataSet.elements[attr]; var text = ""; if(element !== undefined) { var str = dataSet.string(attr); if(str !== undefined) { text = str; } } $(value).text(text); }); $('span[data-dicomUint]').each(function(index, value) { var attr = $(value).attr('data-dicomUint'); var element = dataSet.elements[attr]; var text = ""; if(element !== undefined) { if(element.length === 2) { text += dataSet.uint16(attr); } else if(element.length === 4) { text += dataSet.uint32(attr); } } $(value).text(text); }); }