### Developer Setup for dcmjs Repository Source: https://github.com/dcmjs-org/dcmjs/blob/master/README.md Clone the dcmjs repository and set up the development environment using pnpm and Node.js 22.13 or newer. This includes installing dependencies, building the project, and running tests. ```bash corepack enable git clone https://github.com/dcmjs-org/dcmjs cd dcmjs pnpm install pnpm run build pnpm test ``` -------------------------------- ### Install git-cz Source: https://github.com/dcmjs-org/dcmjs/blob/master/README.md Installs the git-cz tool globally for managing commit messages. ```bash pnpm add -g git-cz # or: npm install -g git-cz ``` -------------------------------- ### Setup Control Panel with Dataset Information Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap2/index.html Retrieves patient and series information from the loaded dataset to configure the control panel. Assumes at least one dataset is loaded. ```javascript function setupControlPanel() { let panelOptions = { subject: dc0paraMap.datasets[0].PatientID, seriesDescription: dc0paraMap.datasets[0].SeriesDescription, }; createControlPanel(panelOptions); } ``` -------------------------------- ### Initialize DICOMZero and Event Listeners Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap/index.html Initializes DICOMZero instances for managing DICOM data and sets up event listeners for file drops and window resizing. This is the main setup for handling DICOM data. ```javascript function status(statusMessage) { console.log("status: ", statusMessage); $("#status").text(statusMessage); } // containers for managing background and parametric map data var dc0 = new DICOMZero({ status }); var dc0paraMap = new DICOMZero({ status }); $(document).ready(function() { // // dropZone const dropZone = document.getElementById("dropZone"); window.addEventListener("resize", function() { dropZone.width = window.innerWidth; dropZone.height = window.innerHeight; }); window.dispatchEvent(new Event("resize")); function handleFileDrop(e) { let evt = e.originalEvent; evt.stopPropagation(); evt.preventDefault(); // // if one file dropped, and we already have a viewer, // check if it's a parametric map and overlay. // Also special case for two files that might be a multiframe and parametric map. // Otherwise reset and load the dropped data. if (dc0.viewer && evt.dataTransfer.files.length == 1) { dc0.handleDataTransferFileAsDataset(evt.dataTransfer.files[0], { doneCallback: function(dataset) { console.log("callback " + dataset.SOPClassUID); if ( dataset.SOPClassUID == dcmjs.data.DicomMetaDictionary.sopClassUIDsByName[ "ParametricMapStorage" ] ) { console.log("add call paramatric map to viewer..."); dc0.viewer.addParametricMapOverlay(dataset, "colorbar"); console.log("add call to viewer successfull"); dc0paraMap.datasets = [dataset]; dc0.viewer.render(); setupControlPanel(); } } }); } else { resetDICOMzero(); dc0.dataTransfer = { files: [] }; for ( let fileIndex = 0; fileIndex < evt.dataTransfer.files.length; fileIndex++ ) { dc0.dataTransfer.files[fileIndex] = evt.dataTransfer.files[fileIndex]; } status(`Got ${dc0.dataTransfer.files.length} files...`); dc0.readOneFile(displayDatasets, status); } } $("#dropZone").bind("drop", handleFileDrop); // // Dataset controls $(".datasetControl").attr("disabled", true); function resetDICOMzero() { if (dc0.viewer) { dc0.viewer.reset(); dc0.viewer.removeElement(); } dc0.reset(); $("#datasetSlider").val(0); $("#datasetSlider").attr("max", 0); } function setOffset(offset) { $("#frameOffset").text(offset); $("#datasetSlider").val(offset); dc0.viewer.index = offset; } function frameCount() { if (dcmjs.normalizers.Normalizer.isMultiframeDataset(dc0.datasets[0])) { return dc0.datasets[0].NumberOfFrames; } else { return dc0.datasets.length; } } function displayDatasets() { status("Displaying..."); dc0.datasets.sort(function(a, b) { return Number(a.InstanceNumber) - Number(b.InstanceNumber); }); $("#datasetSlider").attr("min", 0); $("#datasetSlider").attr("max", frameCount() - 1); $(".datasetControl").attr("disabled", false); options = { callback: function() { togglePlayMode(); } }; status("Setting up viewer..."); dc0.viewer = new Viewer(dc0.datasets); dc0.viewer.display("#dicomImages", options); } $("#datasetSlider").on("input", function() { var offset = $("#datasetSlider").val(); setOffset(offset); }); function playStep() { var offset = Number($("#datasetSlider").val()); offset += 1; if (offset > frameCount() - 1) { offset = 0; } setOffset(offset); if ($("#datasetPlayStop").text() === "Stop") { window.requestAnimationFrame(playStep); } } function playStop() { $("#datasetPlayStop").text("Play"); } function playStart() { $("#datasetPlayStop").text("Stop"); window.requestAnimationFrame(playStep); } function togglePlayMode() { if ($("#datasetPlayStop").text() === "Stop") { playStop(); } else { playStart(); } } $("#datasetPlayStop").on("click", togglePlayMode); function progressStatus(progressEvent) { status( `Downloaded ${(progressEvent.loaded / 1024 / 1024).toFixed( 2 )} MB so far...` ); } /** * Downloads some demo data with the dicom web client * which uses asynchronous xmlhttp-requests to retrieve byte arrays from a server. */ function drawSampleDatasets() { const client = new DICOMwebClient.api.DICOMwebClient({ url: "noURL" }); status("Downloading data..."); let baseImageURL = "https://s3.amazonaws.com/IsomicsPublic/SampleData/dicomPM/"; let imagesCount = 45; for (let i = 45; i < 89; i++) { let url = baseImageURL + "0000" + i + ".dcm"; client._httpGet(url, {}, "arraybuffer").then(arrayBuffer => { let dataset = DICOMZero.datasetFromArrayBuffer(arrayBuffer); dc0.datasets.push(dataset); if (i == 88) { displayDatasets(); status("waiting for parametric map..."); } }); } let basePMURL = "https://s3.amazonaws.com/IsomicsPublic/SampleData/d"; } }); ``` -------------------------------- ### Setup Control Panel Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap/index.html Sets up the control panel for the DICOM viewer, using patient and series information from the loaded datasets. This function is called after the parametric map is loaded. ```javascript function setupControlPanel() { let panelOptions = { subject: dc0paraMap.datasets[0].PatientID, seriesDescription: dc0paraMap.datasets[0].SeriesDescription }; createControlPanel(panelOptions); } ``` -------------------------------- ### Common Pattern: Read Multiple DICOM Files Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Example function demonstrating how to efficiently read multiple DICOM files concurrently using `Promise.all`. ```APIDOC ### Read Multiple Files ```javascript async function readMultipleFiles(arrayBuffers) { const results = await Promise.all( arrayBuffers.map(async (buffer) => { const reader = new AsyncDicomReader(); reader.stream.setData(buffer); await reader.readFile(); return { meta: reader.meta, dataset: reader.dict }; }) ); return results; } ``` ``` -------------------------------- ### Commit with git-cz Source: https://github.com/dcmjs-org/dcmjs/blob/master/README.md Example of using git-cz to create a commit with a specified type and subject. ```bash git-cz --non-interactive --type=fix --subject="commit message" ``` -------------------------------- ### Common dcmjs Developer Tasks Source: https://github.com/dcmjs-org/dcmjs/blob/master/README.md Execute common development tasks for dcmjs, such as building examples, linting, and formatting code. Ensure pnpm is used for all operations. ```bash pnpm run build:examples # Rollup build + copy bundles into examples/js pnpm run lint # ESLint (writes fixes) pnpm run format # Prettier (writes) pnpm run format:check # Prettier (check only) ``` -------------------------------- ### Install dcmjs in Node.js (pnpm) Source: https://github.com/dcmjs-org/dcmjs/blob/master/README.md Add dcmjs to your Node.js project using pnpm. Use the 'dev' tag for the latest code merged to master. ```bash pnpm add dcmjs # latest stable release pnpm add dcmjs@dev # latest code merged to master ``` -------------------------------- ### Setup DICOM Control Panel Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/display2/index.html Initializes and configures the control panel for DICOM segmentation data. It extracts segment information, including colors and labels, and sets up callbacks for visibility and click events. ```javascript function setupControlPanel() { let panelOptions = { subject: dc0seg.datasets[0].PatientID, seriesDescription: dc0seg.datasets[0].SeriesDescription, onSegmentVisibilityChanged: (segmentNumber, visibility) => { console.log(`toggle ${segmentNumber}`); dc0.viewer.layerVisibility(segmentNumber, visibility); }, onSegmentClicked: segmentNumber => { playStop(); scrollToSegment(segmentNumber); }, segments: [] }; let segmentSequence = dc0seg.datasets[0].SegmentSequence; if (segmentSequence.constructor.name != "Array") { segmentSequence = [segmentSequence]; } let hexColor = `#00cc99`; segmentSequence.forEach(segment => { if (segment.RecommendedDisplayCIELabValue) { let rgb = dcmjs.data.Colors.dicomlab2RGB( segment.RecommendedDisplayCIELabValue ); rgb = rgb.map(e => Math.round(e * 255).toString(16)); // convert to hex rgb = rgb.map(e => (e.length < 2 ? "0" + e : e)); // zero pad hexColor = `#${rgb[0]}${rgb[1]}${rgb[2]}`; } let anatomicRegion = segment.AnatomicRegionSequence || { CodeMeaning: "Undefined" }; panelOptions.segments.push({ segmentNumber: segment.SegmentNumber, label: `${segment.SegmentNumber}: ${segment.SegmentLabel} (${anatomicRegion.CodeMeaning }) `, color: hexColor }); }); createControlPanel(panelOptions); } ``` -------------------------------- ### Common Pattern: Extract Specific Tags Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Example function demonstrating how to use AsyncDicomReader to read a DICOM file and extract specific tags from the dataset. ```APIDOC ## Common Patterns ### Extract Specific Tags ```javascript async function extractTags(arrayBuffer, tagList) { const reader = new AsyncDicomReader(); reader.stream.setData(arrayBuffer); await reader.readFile(); const result = {}; for (const tag of tagList) { if (reader.dict[tag]) { result[tag] = reader.dict[tag].Value; } } return result; } // Usage const tags = await extractTags(buffer, [ '00100010', // Patient Name '00100020', // Patient ID '0020000D', // Study Instance UID ]); ``` ``` -------------------------------- ### Working with DICOM Sequences Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Demonstrates accessing nested DICOM sequences, which are automatically parsed into arrays of objects. Example shows accessing Referenced Series Sequence. ```javascript // Sequences are represented as arrays of objects // Example: Referenced Series Sequence async function readWithSequences(arrayBuffer) { const reader = new AsyncDicomReader(); reader.stream.setData(arrayBuffer); await reader.readFile(); // Access sequence items const referencedSeries = reader.dict['00081115']; // Referenced Series Seq if (referencedSeries) { referencedSeries.Value.forEach((item, index) => { console.log(`Series ${index}:`, item['0020000E']); // Series Instance UID }); } return reader; } ``` -------------------------------- ### Define a Logging Filter Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/ArrayBufferExpanderListener.md Example of creating a custom filter that logs processed values before passing them to the next filter in the chain. This demonstrates combining custom logic with the expander. ```javascript const loggingFilter = { value(next, v) { console.log('Processing value:', v); return next(v); } }; ``` -------------------------------- ### Render React Component with Cornerstone Viewport Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/createSegmentationRLERoundtrip/index.html Renders a React component for displaying DICOM images using the 'react-cornerstone-viewport' library. It requires example data, Cornerstone, and CornerstoneTools to be available in the scope. ```javascript var exampleData = { stack: { currentImageIdIndex: 0, imageIds: [ "dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.11.dcm", "dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm" ] } }; var CornerstoneViewport = window["react-cornerstone-viewport"]; var props = { viewportData: exampleData, cornerstone, cornerstoneTools, activeTool: "Brush" }; var app = React.createElement(CornerstoneViewport, props, null); ReactDOM.render(app, document.getElementById("cornerstoneViewport")); ``` -------------------------------- ### Create and Display Viewer Source: https://github.com/dcmjs-org/dcmjs/wiki/Concepts Creates a Viewer instance with specified options and displays it in a target HTML element. Assumes the DICOMZero object contains datasets and the HTML DOM has an element with the ID 'viewer'. ```javascript var viewerOptions = { 'width': 350, 'height': 350, } viewerLeft = new Viewer(dicomZero.datasets, viewerOptions); viewerLeft.display('#viewer'); ``` -------------------------------- ### Volume Viewer Initialization Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/vtkDisplay/index.html Sets up the volume viewer element, creating a vtkGenericRenderWindow, renderer, and attaching it to the specified DOM element. Initializes interaction styles and status messages. ```javascript let volumeViewer; let vtkVolumeActors = []; let vtkImageSliceActors = []; let controllerWidgets = []; let segments = {}; function status (text) { $('p#statusLine').text(text); } $(document).ready(function() { Array.from(document.getElementsByClassName('styleOption')).forEach(element => { element.setAttribute("disabled", "disabled"); }); $('input[name=interactionStyle]').change(event => { setVTKInteractorStyle(event.currentTarget.value); }); document.getElementById('is_slice').setAttribute('checked', 'true'); // // set up the volume viewer // let volumeElement = document.getElementById('volumeViewer'); volumeViewer = vtk.Rendering.Misc.vtkGenericRenderWindow.newInstance({ background: [0, 0, 0] }); const volumeRenderWindow = volumeViewer.getRenderWindow(); const volumeRenderer = volumeViewer.getRenderer(); volumeElement.innerHTML = ''; volumeViewer.setContainer(volumeElement); ``` -------------------------------- ### Common Pattern: Convert DICOM to JSON Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Example function showing how to convert a DICOM file into a JSON object containing both meta-information and the dataset. ```APIDOC ### Convert to JSON ```javascript async function dicomToJson(arrayBuffer) { const reader = new AsyncDicomReader(); reader.stream.setData(arrayBuffer); await reader.readFile(); return { meta: reader.meta, dataset: reader.dict }; } ``` ``` -------------------------------- ### Initialize DICOMZero and Event Listeners Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/display2/index.html Initializes DICOMZero instances for image and segmentation data and sets up event listeners for file drops and window resizing. This is the entry point for handling DICOM data. ```javascript function status(statusMessage) { console.log("status: ", statusMessage); $("#status").text(statusMessage); } // containers for managing background and segmentation data var dc0 = new DICOMZero({ status }); var dc0seg = new DICOMZero({ status }); $(document).ready(function() { // // dropZone const dropZone = document.getElementById("dropZone"); window.addEventListener("resize", function() { dropZone.width = window.innerWidth; dropZone.height = window.innerHeight; }); window.dispatchEvent(new Event("resize")); function handleFileDrop(e) { let evt = e.originalEvent; evt.stopPropagation(); evt.preventDefault(); // // if one file dropped, and we already have a viewer, // check if it's a segmentation and overlay. // Also special case for two files that might be a multiframe and seg. // Otherwise reset and load the dropped data. // if (dc0.viewer && evt.dataTransfer.files.length == 1) { dc0.handleDataTransferFileAsDataset(evt.dataTransfer.files[0], { doneCallback: function(dataset) { if ( dataset.SOPClassUID == dcmjs.data.DicomMetaDictionary.sopClassUIDsByName["Segmentation"] ) { dc0.viewer.addSegmentation(dataset); dc0seg.datasets = [dataset]; dc0.viewer.render(); setupControlPanel(); } } }); } else { resetDICOMzero(); dc0.dataTransfer = { files: [] }; for (let fileIndex = 0; fileIndex < evt.dataTransfer.files.length; fileIndex++) { dc0.dataTransfer.files[fileIndex] = evt.dataTransfer.files[fileIndex]; } status(`Got ${dc0.dataTransfer.files.length} files...`); dc0.readOneFile(displayDatasets, status); } } $("#dropZone").bind("drop", handleFileDrop); // // Dataset controls // $(".datasetControl").attr("disabled", true); function resetDICOMzero() { if (dc0.viewer) { dc0.viewer.reset(); dc0.viewer.removeElement(); } dc0.reset(); $("#datasetSlider").val(0); $("#datasetSlider").attr("max", 0); } function setOffset(offset) { $("#frameOffset").text(offset); $("#datasetSlider").val(offset); dc0.viewer.index = offset; } function frameCount() { if (dcmjs.normalizers.Normalizer.isMultiframeDataset(dc0.datasets[0])) { return dc0.datasets[0].NumberOfFrames; } else { return dc0.datasets.length; } } function displayDatasets() { status("Displaying..."); dc0.datasets.sort(function(a, b) { return Number(a.InstanceNumber) - Number(b.InstanceNumber); }); $("#datasetSlider").attr("min", 0); $("#datasetSlider").attr("max", frameCount() - 1); $(".datasetControl").attr("disabled", false); var options = { callback: function() { // now add the segmentation status("Adding segmentations..."); if (dc0seg.datasets.length > 0) { dc0.viewer.addSegmentation(dc0seg.datasets[0]); setupControlPanel(); } status("Done reading."); togglePlayMode(); } }; status("Setting up viewer..."); dc0.viewer = new Viewer(dc0.datasets); dc0.viewer.display("#dicomImages", options); } function displaySegmentation() { console.log(dc0seg); let imageUID = dc0seg.datasets[0].ReferencedSeriesSequence.SeriesInstanceUID; loadQIICRImages(imageUID); } $("#datasetSlider").on("input", function() { var offset = $("#datasetSlider").val(); setOffset(offset); }); function playStep() { var offset = Number($("#datasetSlider").val()); offset += 1; if (offset > frameCount() - 1) { offset = 0; } setOffset(offset); if ($("#datasetPlayStop").text() === "Stop") { window.requestAnimationFrame(playStep); } } function playStop() { $("#datasetPlayStop").text("Play"); } function playStart() { $("#datasetPlayStop").text("Stop"); window.requestAnimationFrame(playStep); } function togglePlayMode() { if ($("#datasetPlayStop").text() === "Stop") { playStop(); } else { playStart(); } } $("#datasetPlayStop").on("click", togglePlayMode); function progressStatus(progressEvent) { status( `Downloaded ${(progressEvent.loaded / 1024 / 1024).toFixed(2)} MB so far...` ); } // // download and process functions // resetDICOMzero(); let segmentNumberToOffsetMap = []; function scrollToSegment(segmentNumber) { if (segmentNumberToOffsetMap.length == 0) { status("Calculating segment locations"); let perSegmentISList = []; dc0seg.datasets[0].PerFrameFunctionalGroupsSequence.forEach( (perFrameGroup) => { let segment = perFrameGroup.SegmentIdentificationSequence.ReferencedSegmentNumber; if (!perSegmentISList[segment]) { perSegmentISList[segment] = []; } let isCoordinate = Number( perFrameGroup.PlanePositionSequence.ImagePositionPatient[2] ); perSegmentISList[segment].push(isCoordinate); } ); } } ``` -------------------------------- ### Initialize and Configure DICOMZero Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/display/index.html Initializes the DICOMZero object and sets up the document ready event handler. It configures the drop zone, disables dataset controls initially, and sets the dimensions for the DICOM dump area. ```javascript var dc0 = new DICOMZero(); $(document).ready(function() { const dropZone = document.getElementById("dropZone"); $(".datasetControl").attr("disabled", true); $(".dicomDump").css("height", "900px"); $(".dicomDump").css("max-height", "900px"); ``` -------------------------------- ### Initialize and Configure DICOMZero Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/tcia/index.html Initializes the DICOMZero object and sets up the UI elements for handling DICOM file drops and displaying dataset information. It also configures event listeners for file drops and window resizing. ```javascript var dc0 = new dcmjs.data.DICOMZero(); $(document).ready(function() { const dropZone = document.getElementById("dropZone"); $(".datasetControl").attr("disabled", true); $(".dicomDump").css("height", "900px"); $(".dicomDump").css("max-height", "900px"); function resetDICOMzero() { if (dc0.viewer) { dc0.viewer.removeElement(); } dc0.reset(); } function displayDataset(offset) { $("#dicomData").text(JSON.stringify(dc0.datasets[offset], null, 2)); } function setOffset(offset) { $("#datasetOffset").text(offset); $("#datasetSlider").val(offset); displayDataset(offset); dc0.viewer.index = offset; } function displayDatasets() { status("Done reading..."); dc0.datasets.sort(function(a, b) { return Number(a.InstanceNumber) - Number(b.InstanceNumber); }); $("#datasetSlider").attr("max", dc0.datasets.length - 1); $(".datasetControl").attr("disabled", false); displayDataset(0); dc0.viewer = new Viewer(dc0.datasets); dc0.viewer.display("#dicomImages"); let onNewImage = function(event) { setOffset(dc0.viewer.index); }; $(dc0.viewer.element).on("CornerstoneNewImage", onNewImage); } $("#datasetSlider").on("input", function() { var offset = $("#datasetSlider").val(); setOffset(offset); }); function playStep() { var offset = 1 + Number($("#datasetSlider").val()); if (offset > dc0.datasets.length - 1) { offset = 0; } setOffset(offset); if ($("#datasetPlayStop").text() === "Stop") { window.requestAnimationFrame(playStep); } } $("#datasetPlayStop").on("click", function() { if ($("#datasetPlayStop").text() === "Stop") { $("#datasetPlayStop").text("Play"); } else { $("#datasetPlayStop").text("Stop"); window.requestAnimationFrame(playStep); } }); function status(s) { console.log("status: ", s); $("#status").text(s); } resetDICOMzero(); window.addEventListener("resize", function() { dropZone.width = window.innerWidth; dropZone.height = window.innerHeight; }); window.dispatchEvent(new Event("resize")); function handleFileDrop(e) { let evt = e.originalEvent; evt.stopPropagation(); evt.preventDefault(); resetDICOMzero(); dc0.dataTransfer = { files: [] }; for ( let fileIndex = 0; fileIndex < evt.dataTransfer.files.length; fileIndex++ ) { dc0.dataTransfer.files[fileIndex] = evt.dataTransfer.files[fileIndex]; } status(`Got ${dc0.dataTransfer.files.length} files...`); dc0.readOneFile(displayDatasets, status); } $("#dropZone").bind("drop", handleFileDrop); }); ``` -------------------------------- ### Initialize DICOMWEB Client Source: https://github.com/dcmjs-org/dcmjs/wiki/Concepts Initializes the DICOMWEB client with a progress callback and the root URL of the PACS server. The rootURL parameter is mandatory. ```javascript function downloadProgress(progressEvent) { status(`Downloaded ${(progressEvent.loaded / 1024 / 1024).toFixed(2)} MB so far... `); } const rootURL = 'http://:/dcm4chee-arc/aets/DCM4CHEE'; const dicomweb = new dcmjs.DICOMWEB({ progressCallback: downloadProgress, rootURL: rootURL, }); ``` -------------------------------- ### Initialize DICOMZero and QIICR Data Structures Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/qiicr/index-dev.html Initializes DICOMZero instances for handling DICOM data and segmentation, and sets up the qiicr object to store segmentation tables and patient information. ```javascript var dc0 = new DICOMZero(); var dc0seg = new DICOMZero(); var qiicr = { segTable: {}, // fetched as zipped json patients: { // parsed to list of name,uid objects for each patient seg } }; ``` -------------------------------- ### Get Image IDs for Multiframe Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/cornerstoneDICOMSR/index.html Generates an array of imageIds for multiframe DICOM images. It iterates through frames and constructs imageIds with frame parameters, including segment numbers if available. ```javascript // // creates an array of per-frame imageIds in the form needed for cornerstone processing. // function getImageIds(multiframe, baseImageId) { const imageIds = []; const numFrames = Number(multiframe.NumberOfFrames); for (let i = 0; i < numFrames; i++) { let segNum; if ( multiframe.PerFrameFunctionalGroupsSequence[i] .SegmentIdentificationSequence ) { segNum = multiframe.PerFrameFunctionalGroupsSequence[i] .SegmentIdentificationSequence .ReferencedSegmentNumber; } const imageId = baseImageId + "?frame=" + i; imageIds.push(imageId); } return imageIds; } ``` -------------------------------- ### Initialize and Control DICOM Viewer Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/qiicr/index.html Initializes DICOMZero for managing background and segmentation data, and sets up viewer controls. Requires a status callback function. ```javascript function status(statusMessage) { console.log("status: ", statusMessage); $("#status").text(statusMessage); } // containers for managing background and segmentation data var dc0 = new DICOMZero({ status }); var dc0seg = new DICOMZero({ status }); ``` -------------------------------- ### Initialize and Configure DICOMZero for Parametric Maps Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap2/index.html Initializes a DICOMZero instance to manage parametric map data and sets up a status callback. This is typically used when handling file drops. ```javascript function status(statusMessage) { console.log('status: ', statusMessage); $('#status').text(statusMessage); } // container for managing parametric map data var dc0paraMap = new DICOMZero({ status }); ``` -------------------------------- ### Instantiate AsyncDicomReader Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Create a new instance of AsyncDicomReader. Options can be passed to control endianness and buffer clearing. ```javascript new AsyncDicomReader(options) ``` -------------------------------- ### Load Parametric Map and Display Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap/index.html Loads a parametric map from a URL, adds it as an overlay to the DICOM viewer, and renders the viewer. Requires DICOMZero and a client object. ```javascript let basePMURL = "/data/dicom/icomPM/ldMap_axial_cap.dcm"; let options = { BulkDataURI: basePMURL }; client._httpGet(basePMURL, {}, "arraybuffer").then(arrayBuffer => { let dataset = DICOMZero.datasetFromArrayBuffer(arrayBuffer); dc0paraMap.datasets.push(dataset); console.log("parametric map downloaded"); dc0.viewer.addParametricMapOverlay(dataset, "colorbar"); dc0.viewer.render(); setupControlPanel(); status("Done loading."); }); ``` -------------------------------- ### Sequence of Listener Calls for Parsing Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Details the sequence of calls made to a `DicomMetadataListener` during the parsing of a DICOM file by `AsyncDicomReader`. This includes starting and ending objects, adding tags, and handling nested sequences. ```javascript // Start of dict parsing (after fmi) listener.dict ||= {}; listener.startObject(this.dict); // listener.current.level is 0 - top level object awaiting tags // For every tag: listener.addTag(tagHexString, tagInfo); // listener.current.level is 1 - top level attributing awaiting value if( isSequence ) { // For sequences, recursively: listener.startObject([]); // listener.current.level will be 2 for(const child of sequence) { listener.startObject(); // listener.current.level will be 3 ... deliver child to listener listener.pop(); } listener.pop(); } else { // For each value in the tag: values.forEach(value => listener.value(value)); // listener.current should record the value in some way } listener.pop(); ``` -------------------------------- ### Extract Specific DICOM Tags Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md An example function demonstrating how to extract specific tags from a DICOM file's ArrayBuffer. It initializes an AsyncDicomReader, reads the file, and then iterates through a provided list of tags to collect their values. ```javascript async function extractTags(arrayBuffer, tagList) { const reader = new AsyncDicomReader(); reader.stream.setData(arrayBuffer); await reader.readFile(); const result = {}; for (const tag of tagList) { if (reader.dict[tag]) { result[tag] = reader.dict[tag].Value; } } return result; } // Usage const tags = await extractTags(buffer, [ '00100010', // Patient Name '00100020', // Patient ID '0020000D', // Study Instance UID ]); ``` -------------------------------- ### Create Control Panel Configuration Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap2/index.html Defines a function to create a control panel, accepting options for ID, subject, and series description. This is a placeholder for UI generation. ```javascript function createControlPanel(options = {}) { options.id = options.id || "controlPanel"; options.subject = options.subject || "Subject not specified"; options.seriesDescription = options.seriesDescription || "Series Description not specified"; } ``` -------------------------------- ### Handle File Drops for DICOM Data Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap2/index.html Sets up a drop zone to handle dropped files, processes them using DICOMZero, and initiates display. Ensure the drop zone element exists in your HTML. ```javascript const dropZone = document.getElementById('dropZone'); window.addEventListener('resize', function () { dropZone.width = window.innerWidth; dropZone.height = window.innerHeight; }); window.dispatchEvent(new Event('resize')); function handleFileDrop(e) { let evt = e.originalEvent; evt.stopPropagation(); evt.preventDefault(); resetDICOMzero(); dc0paraMap.dataTransfer = { files: [] }; for (let fileIndex = 0; fileIndex < evt.dataTransfer.files.length; fileIndex++) { dc0paraMap.dataTransfer.files[fileIndex] = evt.dataTransfer.files[fileIndex]; } status(`Got ${dc0paraMap.dataTransfer.files.length} files...`); dc0paraMap.readOneFile(displayDatasets, status); } $('#dropZone').bind('drop', handleFileDrop); ``` -------------------------------- ### Read, Modify, and Write DICOM File in Node.js Source: https://github.com/dcmjs-org/dcmjs/wiki/dcmjs-with-nodejs Use this snippet to read a DICOM file from the local file system, modify its tags, and write the changes to a new file. Ensure you have the 'dcmjs' and 'fs' modules installed. ```javascript const dcmjs = require("dcmjs"); const fs = require("fs"); const filePath = "/Users/pieper/data/public-dicom/MRHead-multiframe+seg/MRHead-multiframe.dcm" let arrayBuffer = fs.readFileSync(filePath).buffer; let DicomDict = dcmjs.data.DicomMessage.readFile(arrayBuffer); const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(DicomDict.dict); console.log(dataset) dataset.PatientName = "Name^Somebody's" dataset.ImagePositionPatient = [0, 0, 0]; dataset.ProcedureCodeSequence = { CodeValue: "M2197", CodingSchemeDesignator: "GEIIS", CodeMeaning: "BWH MR PELVIS WWO CONTRAST M2197", }; DicomDict.dict = dcmjs.data.DicomMetaDictionary.denaturalizeDataset(dataset); let new_file_WriterBuffer = DicomDict.write(); fs.writeFileSync("/tmp/file.dcm", new Buffer(new_file_WriterBuffer)); ``` -------------------------------- ### Instantiate DicomMetadataListener with ArrayBufferExpanderFilter and Logging Filter Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/ArrayBufferExpanderListener.md Shows how to combine the ArrayBufferExpanderFilter with a custom logging filter. The filters are applied in the order they are provided to the constructor, with the expander running first. ```javascript const listener = new DicomMetadataListener( ArrayBufferExpanderFilter, loggingFilter ); ``` -------------------------------- ### Download and Display Sample Data Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/vtkDisplay/index.html Initiates the download of sample DICOM data from AWS S3 for background and segmentation tasks, then displays them. Note: The MR sample URL is incomplete in the source. ```javascript function downloadAndDisplaySampleData() { const urlRoot = 'https://s3.amazonaws.com/IsomicsPublic/SampleData/rsna2017/seg/task2/' let backgroundURL = urlRoot + 'PT-multiframe.dcm'; let segURL = urlRoot + 'SEG/tumor_User1_Manual_Trial1.dcm'; backgroundURL = 'https://s3.amazonaws.com/IsomicsPublic/SampleData/MR ``` -------------------------------- ### Initialize React Cornerstone Viewport Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/cornerstoneDICOMSR/index.html Initializes the React Cornerstone Viewport component with specified tool configurations and data. It sets up available tools and the active tool for user interaction. ```javascript "use strict"; var exampleData = { stack: { currentImageIdIndex: 0, imageIds: [ "dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.11.dcm", "dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm" ] } }; var CornerstoneViewport = window["react-cornerstone-viewport"]; var props = { viewportData: exampleData, cornerstone, cornerstoneTools, activeTool: "Length", availableTools: [ // Mouse { name: "Length", mouseButtonMasks: [1] }, { name: "Zoom", mouseButtonMasks: [2] }, { name: "Pan", mouseButtonMask: [4] }, { name: "Bidirectional", mouseButtonMasks: [1] }, { name: "EllipticalRoi", mouseButtonMasks: [1] }, { name: "ArrowAnnotate", mouseButtonMasks: [1] }, // Scroll { name: "StackScrollMouseWheel" } ] }; var app = React.createElement(CornerstoneViewport, props, null); ReactDOM.render( app, document.getElementById("cornerstoneViewport") ); ``` -------------------------------- ### Read Preamble and DICM Marker Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Asynchronously reads the 128-byte preamble and the 'DICM' marker from the file. Returns a promise that resolves to true if the marker is found. ```javascript await reader.readPreamble() ``` -------------------------------- ### Process and Display All DICOM Datasets Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/display/index.html Sorts the loaded DICOM datasets by InstanceNumber, configures the slider, enables controls, displays the first dataset, initializes the viewer, and sets up an event listener for image changes. ```javascript function displayDatasets() { status("Done reading..."); dc0.datasets.sort(function(a, b) { return Number(a.InstanceNumber) - Number(b.InstanceNumber); }); $("#datasetSlider").attr("max", dc0.datasets.length - 1); $(".datasetControl").attr("disabled", false); displayDataset(0); dc0.viewer = new Viewer(dc0.datasets); dc0.viewer.display("#dicomImages"); let onNewImage = function(event) { setOffset(dc0.viewer.index); }; $(dc0.viewer.element).on("CornerstoneNewImage", onNewImage); } ``` -------------------------------- ### Download and Display Sample Parametric Map Data Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/displayParametricMap2/index.html Downloads a sample parametric map from a URL using DICOMwebClient, processes it, and displays it. This function is triggered by a button click. ```javascript function drawSampleDatasets() { const client = new DICOMwebClient.api.DICOMwebClient({"url" : "noURL"}); // TODO: change URL to ADC map example let basePMURL = "https://s3.amazonaws.com/IsomicsPublic/SampleData/paramap.dcm"; client._httpGet(basePMURL, {}, "arraybuffer").then(arrayBuffer => { let dataset = DICOMZero.datasetFromArrayBuffer(arrayBuffer); dc0paraMap.datasets.push(dataset); console.log("parametric map downloaded") displayDatasets(); dc0paraMap.viewer.addParametricMap(dataset, "colorbar"); dc0paraMap.viewer.render(); setupControlPanel(); status("Done loading.") }); } $('#sampleData').click(function (event) { drawSampleDatasets(); $('#sampleData').attr('disabled', true); }); ``` -------------------------------- ### Initialize Segment and Labelmap Selectors Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/createSegmentation/index.html Populates dropdowns for selecting segments and active labelmaps. Assumes the existence of HTML elements with IDs 'switchSegment' and 'switchActiveLabelmap'. ```javascript "use strict"; var process = { env: { NODE_ENV: "production" } }; window.process = process; let metaData = {}; const switchSegment = document.getElementById('switchSegment'); for (let i = 1 ; i <= 255; i++) { const option = document.createElement("option"); option.text = i; option.value = i; switchSegment.add(option); } document.getElementById("switchSegment")[0].selected = true; document.getElementById("switchActiveLabelmap")[0].selected = true; ``` -------------------------------- ### Implement Raw Binary Data Listener Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Extend `DicomMetadataListener` to request and collect raw binary data for specified tags. Initialize chunk collectors in `addTag` and combine fragments in `pop`. ```javascript class RawBinaryListener extends DicomMetadataListener { constructor(tagsToReceiveRaw = []) { super(); this.tagsToReceiveRaw = new Set(tagsToReceiveRaw); this.rawDataReceived = {}; this.rawChunks = {}; } addTag(tag, tagInfo) { // Call the parent implementation const result = super.addTag(tag, tagInfo); // Request raw binary data for specific tags if (this.tagsToReceiveRaw.has(tag)) { // Initialize chunk collector for this tag this.rawChunks[tag] = []; return { expectsRaw: true }; } return result; } value(v) { // Track raw binary data received (may be delivered in multiple chunks) if (this.current && this.tagsToReceiveRaw.has(this.current.tag)) { if (v instanceof ArrayBuffer) { this.rawChunks[this.current.tag].push(v); } } return super.value(v); } pop() { // When tag is complete, combine chunks if needed if (this.current && this.tagsToReceiveRaw.has(this.current.tag)) { const tag = this.current.tag; const chunks = this.rawChunks[tag]; if (chunks.length === 1) { this.rawDataReceived[tag] = chunks[0]; } else if (chunks.length > 1) { // Combine multiple chunks into a single ArrayBuffer const totalSize = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0); const combined = new Uint8Array(totalSize); let offset = 0; for (const chunk of chunks) { combined.set(new Uint8Array(chunk), offset); offset += chunk.byteLength; } this.rawDataReceived[tag] = combined.buffer; } delete this.rawChunks[tag]; } return super.pop(); } } ``` ```javascript // Usage const listener = new RawBinaryListener([ '00100010', // Patient Name '00080060', // Modality '00201041', // Slice Location ]); const reader = new AsyncDicomReader(); reader.stream.setData(arrayBuffer); await reader.readFile({ listener }); // Access raw binary data (now combined from all chunks) console.log(listener.rawDataReceived); ``` -------------------------------- ### DICOMweb Image Display Logic Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/IHEInvokeImageDisplay/index.html This JavaScript code sets up the DICOMweb client, handles study and series data retrieval, and displays images. It requires a DICOMweb server and a Viewer library. ```javascript function status(statusMessage) { console.log("status: ", statusMessage); $("#status").text(statusMessage); } let dicomweb; let seriesContainers = []; $(document).ready(function() { function displayDatasets(seriesContainer) { seriesContainer.datasets.sort(function(a, b) { return Number(a.InstanceNumber) - Number(b.InstanceNumber); }); let dataset0 = seriesContainer.datasets[0]; let seriesElementID = dataset0.SeriesInstanceUID.replace(/\./g, "_"); let seriesDescription = dataset0.SeriesDescription; status(`Displaying ${seriesDescription}...`); let options = { callback: function() { status(`Done reading ${seriesDescription}.`); } }; status(`Setting up viewer for ${seriesDescription}...`); let viewerElement = document.createElement("div"); viewerElement.setAttribute("id", seriesElementID); document.querySelector("#dicomImages").appendChild(viewerElement); let captionElement = document.createElement("p"); captionElement.innerText = seriesDescription; document.querySelector("#dicomImages").appendChild(captionElement); seriesContainer.viewer = new Viewer(seriesContainer.datasets); seriesContainer.viewer.display(`#${seriesElementID}`, options); } function downloadProgress(progressEvent) { status( `Downloaded ${(progressEvent.loaded / 1024 / 1024).toFixed( 2 )} MB so far...` ); } // // download and process functions // dicomweb = new dcmjs.DICOMWEB({ progressCallback: downloadProgress, rootURL: "http://quantome.org:4242/dcm4chee-arc/aets/DCM4CHEE" }); function loadStudies(studyResponses) { let studyResponse = dcmjs.data.DicomMetaDictionary.namifyDataset( studyResponses[0] ); let studyInstanceUID = studyResponse.StudyInstanceUID.Value[0]; dicomweb.series(studyInstanceUID).then(seriesResponses => { seriesResponses.forEach(seriesResponse => { seriesResponse = dcmjs.data.DicomMetaDictionary.namifyDataset(seriesResponse); let seriesInstanceUID = seriesResponse.SeriesInstanceUID.Value[0]; dicomweb .instances(studyInstanceUID, seriesInstanceUID) .then(instancesResponses => { let seriesContainer = new DICOMZero(); seriesContainers.push(seriesContainer); instancesResponses.forEach(instancesResponse => { let response = dcmjs.data.DicomMetaDictionary.namifyDataset(instancesResponse); let sopInstanceUID = response.SOPInstanceUID.Value[0]; dicomweb .instance( studyInstanceUID, seriesInstanceUID, sopInstanceUID ) .then(arrayBuffer => { let dataset = seriesContainer.datasetFromArrayBuffer( arrayBuffer ); seriesContainer.datasets.push(dataset); if ( seriesContainer.datasets.length == instancesResponses.length ) { displayDatasets(seriesContainer); } }); }); }); }); }); } // // handle url parameters // - use a default if none specified // let searchParams = new URLSearchParams( window.location.search.substring(1) ); let requestType = searchParams.get("requestType"); if (requestType == "PATIENT") { let patientID = searchParams.get("patientID"); status(patientID); dicomweb.studies(patientID).then(loadStudies); } else if (requestType == "STUDY") { let studyID = searchParams.get("studyUID"); status(studyID); loadStudies([studyUID]); } }); ``` -------------------------------- ### AsyncDicomReader Constructor Source: https://github.com/dcmjs-org/dcmjs/blob/master/docs/AsyncDicomReader-skill.md Initializes a new instance of AsyncDicomReader. Options can be provided to control endianness and buffer clearing, and additional options are passed to the underlying ReadBufferStream. ```APIDOC ## Constructor AsyncDicomReader ### Description Initializes a new instance of AsyncDicomReader. ### Parameters #### Options - **isLittleEndian** (boolean): Force specific endianness. - **clearBuffers** (boolean): Enable automatic buffer clearing (default: true). - Additional options passed to `ReadBufferStream`. ``` -------------------------------- ### Render Window in DCMJS Source: https://github.com/dcmjs-org/dcmjs/blob/master/examples/vtkDisplay/index.html Renders the content of a window. Ensure the window is properly initialized before calling this function. ```javascript etRenderWindow().render(); ```