新增 dcm 等医疗数位影像预览

This commit is contained in:
gaoxiongzaq
2023-04-20 16:06:01 +08:00
parent 76043a5b46
commit 83e20ac83c
19 changed files with 86959 additions and 1 deletions

View File

@@ -0,0 +1,159 @@
class DICOMZero {
constructor(options={}) {
this.status = options.status || function() {};
this.reset();
}
reset() {
this.mappingLog = [];
this.dataTransfer = undefined;
this.datasets = [];
this.readers = [];
this.arrayBuffers = [];
this.files = [];
this.fileIndex = 0;
this.context = {patients: []};
}
static datasetFromArrayBuffer(arrayBuffer) {
let dicomData = dcmjs.data.DicomMessage.readFile(arrayBuffer);
let dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomData.dict);
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(dicomData.meta);
return(dataset);
}
// return a function to use as the 'onload' callback for the file reader.
// The function takes a progress event argument and it knows (from this class instance)
// when all files have been read so it can invoke the doneCallback when all
// have been read.
getReadDICOMFunction(doneCallback, statusCallback) {
statusCallback = statusCallback || console.log;
return progressEvent => {
let reader = progressEvent.target;
let arrayBuffer = reader.result;
this.arrayBuffers.push(arrayBuffer);
let dicomData;
try {
dicomData = dcmjs.data.DicomMessage.readFile(arrayBuffer);
let dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomData.dict);
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(dicomData.meta);
this.datasets.push(dataset);
} catch (error) {
console.error(error);
statusCallback("skipping non-dicom file");
}
let readerIndex = this.readers.indexOf(reader);
if (readerIndex < 0) {
reject("Logic error: Unexpected reader!");
} else {
this.readers.splice(readerIndex, 1); // remove the reader
}
if (this.fileIndex === this.dataTransfer.files.length) {
statusCallback(`Normalizing...`);
try {
this.multiframe = dcmjs.normalizers.Normalizer.normalizeToDataset(this.datasets);
} catch (e) {
console.error('Could not convert to multiframe');
console.error(e);
}
if (this.multiframe.SOPClassUID == dcmjs.data.DicomMetaDictionary.sopClassUIDsByName['Segmentation']){
statusCallback(`Creating segmentation...`);
try {
this.seg = new dcmjs.derivations.Segmentation([this.multiframe]);
statusCallback(`Created ${this.multiframe.NumberOfFrames} frame multiframe object and segmentation.`);
} catch (e) {
console.error('Could not create segmentation');
console.error(e);
}
} else if (this.multiframe.SOPClassUID == dcmjs.data.DicomMetaDictionary.sopClassUIDsByName['ParametricMapStorage']){
statusCallback(`Creating parametric map...`);
try {
this.pm = new dcmjs.derivations.ParametricMap([this.multiframe]);
statusCallback(`Created ${this.multiframe.NumberOfFrames} frame multiframe object and parametric map.`);
} catch (e) {
console.error('Could not create parametric map');
console.error(e);
}
}
doneCallback();
} else {
statusCallback(`Reading... (${this.fileIndex+1}).`);
this.readOneFile(doneCallback, statusCallback);
}
};
}
// Used for file selection button or drop of file list
readOneFile(doneCallback, statusCallback) {
let file = this.dataTransfer.files[this.fileIndex];
this.fileIndex++;
let reader = new FileReader();
reader.onload = this.getReadDICOMFunction(doneCallback, statusCallback);
reader.readAsArrayBuffer(file);
this.files.push(file);
this.readers.push(reader);
}
handleDataTransferFileAsDataset(file, options={}) {
options.doneCallback = options.doneCallback || function(){};
let reader = new FileReader();
reader.onload = (progressEvent) => {
let dataset = DICOMZero.datasetFromArrayBuffer(reader.result);
options.doneCallback(dataset);
}
reader.readAsArrayBuffer(file);
}
extractDatasetFromZipArrayBuffer(arrayBuffer) {
this.status(`Extracting ${this.datasets.length} of ${this.expectedDICOMFileCount}...`);
this.datasets.push(DICOMZero.datasetFromArrayBuffer(arrayBuffer));
if (this.datasets.length == this.expectedDICOMFileCount) {
this.status(`Finished extracting`);
this.zipFinishCallback();
}
};
handleZip(zip) {
this.zip = zip;
this.expectedDICOMFileCount = 0;
Object.keys(zip.files).forEach(fileKey => {
this.status(`Considering ${fileKey}...`);
if (fileKey.endsWith('.dcm')) {
this.expectedDICOMFileCount += 1;
zip.files[fileKey].async('arraybuffer').then(this.extractDatasetFromZipArrayBuffer.bind(this));
}
});
}
extractFromZipArrayBuffer(arrayBuffer, finishCallback=function(){}) {
this.zipFinishCallback = finishCallback;
this.status("Extracting from zip...");
JSZip.loadAsync(arrayBuffer)
.then(this.handleZip.bind(this));
}
organizeDatasets() {
this.datasets.forEach(dataset => {
let patientName = dataset.PatientName;
let studyTag = dataset.StudyDate + ": " + dataset.StudyDescription;
let seriesTag = dataset.SeriesNumber + ": " + dataset.SeriesDescription;
let patientNames = this.context.patients.map(patient => patient.name);
let patientIndex = patientNames.indexOf(dataset.PatientName);
if (patientIndex == -1) {
this.context.patients.push({
name: dataset.PatientName,
id: this.context.patients.length,
studies: {}
});
}
let studyNames; // TODO - finish organizing
});
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init();
cornerstoneTools.addTool(cornerstoneTools.BidirectionalTool);
cornerstoneTools.addTool(cornerstoneTools.ArrowAnnotateTool);
cornerstoneTools.addTool(cornerstoneTools.EllipticalRoiTool);
function getBlobUrl(url) {
const baseUrl = window.URL || window.webkitURL;
const blob = new Blob([`importScripts('${url}')`], {
type: "application/javascript"
});
return baseUrl.createObjectURL(blob);
}
const config = {
maxWebWorkers: navigator.hardwareConcurrency || 1,
startWebWorkersOnDemand: true,
webWorkerPath: getBlobUrl(
"https://unpkg.com/cornerstone-wado-image-loader/dist/cornerstoneWADOImageLoaderWebWorker.min.js"
),
webWorkerTaskPaths: [],
taskConfiguration: {
decodeTask: {
loadCodecsOnStartup: true,
initializeCodecsOnStartup: false,
codecsPath: getBlobUrl(
"https://unpkg.com/cornerstone-wado-image-loader/dist/cornerstoneWADOImageLoaderCodecs.min.js"
),
usePDFJS: false,
strict: false
}
}
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<title>DCM预览</title>
<#include "*/commonHeader.ftl">
</head>
<style>
.container{
width: 100%;
height: 600px;
max-width: 900px;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: green;
}
</style>
<body>
<#if currentUrl?contains("http://") || currentUrl?contains("https://")>
<#assign finalUrl="${currentUrl}">
<#else>
<#assign finalUrl="${baseUrl}${currentUrl}">
</#if>
<div class="container" id="cornerstoneViewport">
</div>
<script src="dcm/cornerstone.js"></script>
<script src="dcm/cornerstoneMath.js"></script>
<script src="dcm/cornerstoneTools.js"></script>
<script src="dcm/dicomParser.js"></script>
<script src="dcm/cornerstoneWADOImageLoader.bundle.min.js"></script>
<script src="dcm/hammer.min.js"></script>
<script src="dcm/initCornerstone.js"></script>
<script src="dcm/react.development.js" ></script>
<script src="dcm/react-dom.development.js"></script>
<script>
"use strict";
var process = {
env: {
NODE_ENV: "production"
}
};
window.process = process;
</script>
<script src="dcm/index.umd.js"></script>
<script>
var url = '${finalUrl}';
var baseUrl = '${baseUrl}'.endsWith('/') ? '${baseUrl}' : '${baseUrl}' + '/';
if (!url.startsWith(baseUrl)) {
url = baseUrl + 'getCorsFile?urlPath=' + encodeURIComponent(Base64.encode(url));
}
"use strict";
var imageNames = [];
for (var i = 1; i < 546; i++) {
imageNames.push(url);
}
// console.log(url);
var imageIds = imageNames.map(name => {
return 'dicomweb:'+url+'';
});
var imagePromises = imageIds.map(imageId => {
return cornerstone.loadAndCacheImage(imageId);
});
var exampleData = {
stack: {
currentImageIdIndex: 0,
imageIds: imageIds
}
};
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")
);
/*初始化水印*/
window.onload = function () {
initWaterMark();
}
</script>
</body>
</html>

View File

@@ -79,6 +79,7 @@
<li>支持 svg 矢量图像格式文件</li>
<li>支持 mp3,wav,mp4,flv 等音视频格式文件</li>
<li>支持 avi,mov,rm,webm,ts,rm,mkv,mpeg,ogg,mpg,rmvb,wmv,3gp,ts,swf 等视频格式转码预览</li>
<li>支持 dcm 等医疗数位影像预览</li>
</ol>
</div>
<#-- 输入下载地址预览文件 -->

View File

@@ -46,6 +46,16 @@
<div class="page-header">
<h1>版本发布记录</h1>
</div>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">2023年04月20日v4.3.0-SNAPSHOT版本</h3>
</div>
<div class="panel-body">
<div>
1.新增 dcm 等医疗数位影像预览<br>
</div>
</div>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">2023年04月18日v4.2.1 版本</h3>