@@ -1,12 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import { encoder } from './encoder'
|
||||
import { anonymization } from './anonymization'
|
||||
export const dcmUpload = async function(name, file, config){
|
||||
export const dcmUpload = async function (name, file, config) {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
// let blob = await encoder(file, config)
|
||||
// let blob = await fileToBlob(file)
|
||||
let blob = await anonymization(file, config)
|
||||
let blob = await fileToBlob(file)
|
||||
if (config) {
|
||||
blob = await anonymization(file, config)
|
||||
}
|
||||
let res = await Vue.prototype.OSSclient.put(name, blob.blob)
|
||||
resolve({
|
||||
...res,
|
||||
@@ -35,7 +36,7 @@ function fileToBlob(file) {
|
||||
} else {
|
||||
blob = e.target.result
|
||||
}
|
||||
resolve(blob)
|
||||
resolve({ blob })
|
||||
})
|
||||
// FileReader 以 ArrayBuffer 格式 读取 File 对象中数据
|
||||
reader.readAsArrayBuffer(file)
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import * as dicomParser from "dicom-parser";
|
||||
import * as cornerstone from "cornerstone-core";
|
||||
import * as cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
|
||||
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||
import { convertBytes } from "@/utils/dicom-character-set";
|
||||
import Vue from 'vue';
|
||||
let dicom = {
|
||||
PatientName: "x00100010", // 患者姓名
|
||||
PatientId: "x00100020", // 患者ID
|
||||
PatientBirthDate: "x00100030", // 患者出生日期
|
||||
PatientBirthTime: "x00100032", // 患者出生时间
|
||||
PatientSex: "x00100040", // 患者性别
|
||||
PatientWeight: "x00101030", // 患者体重
|
||||
PregnancyStatus: "x001021c0", // 怀孕状态
|
||||
PatientAge: "x00101010", // 患者年龄(做检查时刻的患者年龄,而不是此刻患者的真实年龄)
|
||||
AcquisitionTime: "x00080032",
|
||||
AcquisitionNumber: "x00200012",
|
||||
TriggerTime: "x00181060",
|
||||
AccessionNumber: "x00080050", // 检查号
|
||||
StudyId: "x00200010", // 检查ID
|
||||
StudyInstanceUid: "x0020000d", // 检查实例号
|
||||
StudyDate: "x00080020", // 检查日期
|
||||
StudyTime: "x00080030", // 检查时间
|
||||
Modalities: "x00080061", // 一个检查中含有的不同检查类型
|
||||
BodyPartExamined: "x00080015", // 检查的部位
|
||||
StudyDescription: "x00081030", // 检查的描述
|
||||
InstitutionName: "x00080080",
|
||||
SeriesNumber: "x00200011", // 序列号
|
||||
SeriesInstanceUid: "x0020000e", // 序列实例号
|
||||
Modality: "x00080060", // 检查模态
|
||||
SeriesDescription: "x0008103e", // 检查描述和说明
|
||||
SeriesDate: "x00080021", // 检查日期
|
||||
SeriesTime: "x00080031", // 检查时间
|
||||
SequenceName: "x00180024",
|
||||
ProtocolName: "x00181030",
|
||||
ImagePosition: "x00200032", // 图像位置
|
||||
ImageOrientation: "x00200037", // 图像方位
|
||||
ImagePixelSpacing: "x00181164",
|
||||
SliceThickness: "x00180050", // 层厚
|
||||
SpacingBetweenSlices: "x00180088", // 层间距
|
||||
SliceLocation: "x00201041", // 相对位置
|
||||
MRAcquisition: "x00180023",
|
||||
seriesBodyPartExamined: "x00180015", // 身体部位
|
||||
ImageType: "x00080008",
|
||||
SopInstanceUid: "x00080018", // SOP实例UID
|
||||
ContentDate: "x00080023", // 影像拍摄日期
|
||||
ContentTime: "x00080033", // 影像拍摄时间
|
||||
ImageOrInstanceNumber: "x00200013", // 图像码
|
||||
SamplesPerPixel: "x00280002", // 图像采样率
|
||||
PhotometricInterpretation: "x00280004", // 光度计(对于CT图像,用两个枚举值MONOCHROME1,MONOCHROME2 用来判断图像是否是彩色的;MONOCHROME 1/2是灰度图,RGB则是真彩色图)
|
||||
Rows: "x00280010", // 行数
|
||||
Columns: "x00280011", // 列数
|
||||
NumberOfFrames: "x00280008",
|
||||
PixelSpacing: "x00280030", // 像素间距
|
||||
BitsAllocated: "x00280100", // 分配的位数
|
||||
BitsStored: "x00280101", // 存储的位数
|
||||
HighBit: "x00280102", // 高位
|
||||
PixelRepresentation: "x00280103", // 像素数据的表现类型(一个枚举值,分别为十六进制数0000和0001.0000H = 无符号整型,0001H = 2的补码)
|
||||
WindowCenter: "x00281050", // 窗位
|
||||
WindowWidth: "x002811051", // 窗宽
|
||||
RescaleIntercept: "x00281052", // 截距
|
||||
RescaleSlope: "x00281053", // 斜率
|
||||
RescaleType: "x00281054", // 输出值的单位
|
||||
ParsingFormat: "x00080005",
|
||||
FrameOfReferenceUID: "x00200052",
|
||||
};
|
||||
// 需要设置默认值
|
||||
let defaultKey = ['Rows', 'Columns', 'SliceLocation', 'NumberOfFrames'];
|
||||
// 需要格式解析
|
||||
let pormatParseKey = ['PatientName', 'SeriesDescription', 'StudyDescription'];
|
||||
// 解析dicom文件
|
||||
export const parseDicom = (file, name = false) => {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
try {
|
||||
let data = dicomParser.parseDicom(new Uint8Array(e.target.result));
|
||||
let res = {};
|
||||
if (name && Array.isArray(name)) {
|
||||
name.forEach((item) => {
|
||||
if (dicom[item]) {
|
||||
res[item] = data.string(dicom[item]) || data.uint16(dicom[item]) || data.intString(dicom[item]) || '';
|
||||
}
|
||||
if (item === 'SliceLocation') {
|
||||
res[item] = data.intString(dicom[item])
|
||||
}
|
||||
});
|
||||
} else if (name) {
|
||||
if (dicom[name]) {
|
||||
res[name] = data.string(dicom[name]) || data.uint16(dicom[name]) || data.intString(dicom[name]) || '';
|
||||
if (name === 'SliceLocation') {
|
||||
res[name] = data.intString(dicom[name])
|
||||
}
|
||||
} else {
|
||||
reject("name is inexistence");
|
||||
}
|
||||
} else {
|
||||
Object.keys(dicom).forEach((key) => {
|
||||
res[key] = data.string(dicom[key]) || data.uint16(dicom[key]) || data.intString(dicom[key]) || '';
|
||||
if (key === 'SliceLocation') {
|
||||
res[key] = data.intString(dicom[key])
|
||||
}
|
||||
});
|
||||
}
|
||||
pormatParseKey.forEach(key => {
|
||||
if (res[key]) {
|
||||
const Element = data.elements[dicom[key]];
|
||||
const Bytes = new Uint8Array(
|
||||
data.byteArray.buffer,
|
||||
Element ? Element.dataOffset : 0,
|
||||
Element ? Element.length : 0
|
||||
);
|
||||
res[key] = convertBytes(
|
||||
res.ParsingFormat,
|
||||
Bytes
|
||||
);
|
||||
}
|
||||
})
|
||||
defaultKey.forEach(key => {
|
||||
if (!res[key] && res.hasOwnProperty(key)) {
|
||||
res[key] = 0;
|
||||
}
|
||||
})
|
||||
resolve(res);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
reader.onerror = function (e) {
|
||||
reject(e);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
};
|
||||
// 获取缩略图
|
||||
export const getThumbnail = async (file, params, dicomInfo) => {
|
||||
try {
|
||||
if (dicomInfo.modality !== "SR") {
|
||||
let fileId = cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
|
||||
let blob = await dicomToPng(
|
||||
fileId,
|
||||
dicomInfo.Columns,
|
||||
dicomInfo.Rows
|
||||
);
|
||||
if (!blob) return "";
|
||||
let thumbnailPath = `/${params.TrialId}/TaskImage/${params.SubjectId}/${params.VisitTaskId}/${dicomInfo.StudyInstanceUid}/${dicomInfo.SeriesInstanceUid}.png`;
|
||||
let OSSclient = Vue.prototype.OSSclient;
|
||||
let seriesRes = await OSSclient.put(thumbnailPath, blob);
|
||||
if (seriesRes && seriesRes.url) {
|
||||
return Vue.prototype.$getObjectName(seriesRes.url);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
const canvasToBlob = (canvas) => {
|
||||
return new Promise((resolve) => {
|
||||
canvas.toBlob((blob) => {
|
||||
resolve(blob);
|
||||
});
|
||||
});
|
||||
};
|
||||
const dicomToPng = (imageId, width, height) => {
|
||||
return new Promise((resolve) => {
|
||||
cornerstone.loadImage(imageId).then(async (image) => {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
if (image) {
|
||||
cornerstone.renderToCanvas(canvas, image);
|
||||
// 将 Canvas 图像对象转换为 PNG 格式
|
||||
let blob = await canvasToBlob(canvas);
|
||||
resolve(blob);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
}).catch((reason) => {
|
||||
reason();
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user