irc_web/.svn/pristine/ef/efa2ba3f35987cfcd97479024e1...

898 lines
27 KiB
Plaintext

<template>
<div
id="canvas"
ref="canvas"
v-loading="loading"
element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)"
style="width:100%;height:100%;position:relative;"
class="cornerstone-element"
@contextmenu.prevent="onContextmenu"
>
<div v-show="dicomInfo.series" class="info-series">
<div>Series #{{ dicomInfo.series }}</div>
<div>Image #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div>
</div>
<div v-show="dicomInfo.series" class="info-image">
<div>
P {{ dicomInfo.size }}
<span v-show="dicomInfo.pixel">{{ dicomInfo.pixel }}mm</span>
</div>
<div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div>
<div>WW/WC {{ dicomInfo.wwwc }}</div>
<div>Zoom {{ dicomInfo.zoom }}</div>
<div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div>
<!-- <div v-show="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> -->
</div>
<div class="info-subject">
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div>
<div>{{ dicomInfo.name }}</div>
<!-- <div>{{ dicomInfo.sex }} {{ dicomInfo.age }}</div> -->
<!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> -->
<div>{{ dicomInfo.time }}</div>
</div>
<!-- <div v-show="stack.firstImageLoading" class="load-indicator">
Loading Series #{{ stack.seriesNumber }}...
</div>-->
</div>
</template>
<script>
import Vue from 'vue'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
cornerstoneTools.toolStyle.setToolWidth(2)
cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)')
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ showSVGCursors: true })
cornerstoneTools.init()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
export default {
data() {
return {
loading: false,
canvas: {},
stack: {
seriesId: '',
instanceId: '',
seriesNumber: '',
imageIds: [],
currentImageIdIndex: 0,
firstImageLoading: false
// preventCache: true
},
dicomInfo: {
hospital: '',
pid: '',
name: '',
sex: '',
age: '',
acc: '',
modality: '',
time: '',
series: '',
frame: '',
size: '',
pixel: 0,
thick: 0,
wwwc: '',
zoom: 0,
fps: 5
},
toolState: {
initialized: false,
activeTool: 'none',
dicomInfoVisible: false,
clipPlaying: false,
viewportInvert: false
},
loadImagePromise: null,
AnnotationSync: null,
allROIToolData: {},
type: '',
series: '',
ToolStateManager
}
},
mounted() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : ''
this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener(
'cornerstoneimagerendered',
this.onImageRendered
)
this.canvas.addEventListener(
'cornerstonetoolsclipstopped',
this.onClipStopped
)
},
methods: {
loadImageStack(dicomSeries) {
this.series = dicomSeries
this.stack.seriesId = dicomSeries.seriesId
this.stack.seriesNumber = dicomSeries.seriesNumber
this.stack.imageIds = dicomSeries.imageIds
this.stack.currentImageIdIndex = 0
this.stack.firstImageLoading = true
if (this.toolState.clipPlaying) this.toggleClipPlay()
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const imageId = this.stack.imageIds[this.stack.currentImageIdIndex]
this.stack.instanceId = imageId.split('/')[imageId.split('/').length - 1]
const element = this.$refs.canvas
cornerstone.enable(element)
var scope = this
// cornerstone.imageCache.setMaximumSizeBytes(100000)
this.loading = true
// cornerstone.loadImage(this.stack.imageIds[this.stack.currentImageIdIndex])
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(image => {
this.loading = false
scope.onFirstImageLoaded(image)
}).catch((error) => {
this.loading = false
if (error.error && error.error.message) {
this.$message.error(error.error.message)
}
})
},
onFirstImageLoaded(image) {
const element = this.$refs.canvas
var viewport = cornerstone.getDefaultViewportForImage(this.canvas, image)
cornerstone.displayImage(this.canvas, image, viewport)
if (!this.toolState.initialized) {
this.toolState.initialized = true
const toolButtons = document.querySelectorAll('[data-tool]')
// const scope = this
Array.from(toolButtons).forEach((toolBtn) => {
// Add the tool
const toolName = toolBtn.getAttribute('data-tool')
const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(
element,
apiTool
)
if (!toolAlreadyAddedToElement) {
cornerstoneTools.addToolForElement(element, apiTool)
}
}
// Setup button listener
// Prevent right click context menu for our menu buttons
toolBtn.addEventListener(
'contextmenu',
(event) => event.preventDefault(),
true
)
// Prevent middle click opening a new tab on Chrome & FF
toolBtn.addEventListener(
'auxclick',
(event) => {
if (event.button && event.button === 1) {
event.preventDefault()
}
},
false
)
})
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.StackScrollMouseWheelTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.StackScrollMouseWheelTool
)
}
cornerstoneTools.setToolActiveForElement(
element,
'StackScrollMouseWheel',
{}
)
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.ScaleOverlayTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.ScaleOverlayTool
)
}
cornerstoneTools.setToolActiveForElement(element, 'ScaleOverlay', {})
}
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.OrientationMarkersTool)) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.OrientationMarkersTool
)
}
cornerstoneTools.setToolActiveForElement(
element,
'OrientationMarkers',
{}
)
cornerstoneTools.addStackStateManager(this.canvas, ['stack', 'stackPrefetch', 'playClip'])
cornerstoneTools.addToolState(this.canvas, 'stack', this.stack)
cornerstoneTools.stackPrefetch.enable(this.canvas)
// cornerstoneTools.stackPrefetch.setConfiguration({ maxImagesToPrefetch: Infinity,
// preserveExistingPool: true })
// cornerstoneTools.stackPrefetch.enable(this.canvas)
// this.stack.imageIds.forEach((item, index) => {
// if (index === 0) {
// return
// }
// cornerstone.loadImage(item)
// })
this.stack.firstImageLoading = false
this.toolState.dicomInfoVisible = true
// 重绘历史标记
// if (ToolStateManager.toolState.hasOwnProperty(image.imageId) === true) {
// return
// }
this.stack.instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
},
onNewImage(e) {
e.detail.enabledElement.options = {}
var data = e.detail.image.data
this.dicomInfo.hospital = data.string('x00080080')
// this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040')
this.dicomInfo.acc = data.string('x00080050') // 登记号
this.dicomInfo.modality = data.string('x00080060')
this.dicomInfo.time = this.formatDicomDateTime(
data.string('x00080020'),
data.string('x00080030')
)
this.dicomInfo.series = data.string('x00200011')
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${
this.stack.imageIds.length
}`
this.dicomInfo.size = `${data.uint16('x00280010')}x${data.uint16(
'x00280011'
)}`
// var pixel = data.floatString('x00280030')
if (this.dicomInfo.pixel) {
this.dicomInfo.pixel = this.dicomInfo.pixel.toFixed(2)
}
this.dicomInfo.thick = data.floatString('x00180050') // 切片厚度
if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
}
},
formatDicomDateTime(date, time) {
if (date) {
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
}
if (time) { time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}` }
return time ? `${date} ${time}` : `${date} 00:00:00`
},
onImageRendered(e) {
var viewport = e.detail.viewport
this.dicomInfo.wwwc = `${Math.round(
viewport.voi.windowWidth
)}/${Math.round(viewport.voi.windowCenter)}` // 窗位
this.dicomInfo.zoom = viewport.scale.toFixed(2)
var data = e.detail.image.data
const position = data.string('x00200032')
this.dicomInfo.location = position ? Number(
position.split('\\')[position.split('\\').length - 1]
).toFixed(2) : ''
},
onImageLoaded(e) {
var image = e.detail.image
// var seriesIndex = -1
var seriesUid = image.data.string('x0020000e')
console.log(seriesUid)
},
getToolForElement(toolName) {
var isExist = false
for (var i = 0; i < cornerstoneTools.store.state.tools.length; i++) {
if (cornerstoneTools.store.state.tools[i].name === toolName) {
isExist = true
break
}
}
return isExist
},
onClipStopped() {
this.toolState.clipPlaying = false
},
onKeyPress(e) {
var key = e.detail.keyCode
var keys = { PageUp: 33, PageDown: 34, End: 35, Home: 36 } // Left: 37, Up: 38, Right: 39, Down: 40
if (key < keys.PageUp || key > keys.Home) return
if (key === keys.Home) this.scrollPage(-9999)
else if (key === keys.PageUp) this.scrollPage(-1)
else if (key === keys.PageDown) this.scrollPage(1)
else if (key === keys.End) this.scrollPage(9999)
},
resizeCanvas() {
cornerstone.resize(this.canvas, true)
},
activateCanvas() {
this.canvas.tabIndex = 0
this.canvas.focus()
},
resetViewport() {
this.toolState.viewportInvert = false
var image = cornerstone.getImage(this.canvas)
cornerstone.setViewport(
this.canvas,
cornerstone.getDefaultViewportForImage(this.canvas, image)
)
},
toggleDicomInfo() {
this.toolState.dicomInfoVisible = !this.toolState.dicomInfoVisible
if (this.toolState.dicomInfoVisible) {
cornerstoneTools.orientationMarkers.enable(this.canvas)
} else cornerstoneTools.orientationMarkers.disable(this.canvas)
cornerstone.updateImage(this.canvas)
},
setColormap(colormap) {
const viewport = cornerstone.getViewport(this.canvas)
viewport.colormap = colormap
cornerstone.setViewport(this.canvas, viewport)
cornerstone.updateImage(this.canvas, true)
if (!colormap) {
this.resetRenderCanvase(this.canvas)
}
},
resetRenderCanvase(element) {
const enabledElement = cornerstone.getEnabledElement(element)
enabledElement.renderingTools.colormapId = undefined
enabledElement.renderingTools.colorLut = undefined
const renderCanvas = enabledElement.renderingTools.renderCanvas
const canvasContext = renderCanvas.getContext('2d')
canvasContext.fillStyle = 'white'
canvasContext.fillRect(0, 0, renderCanvas.width, renderCanvas.height)
const renderCanvasData = canvasContext.getImageData(
0,
0,
renderCanvas.width,
renderCanvas.height
)
enabledElement.renderingTools.renderCanvasContext = canvasContext
enabledElement.renderingTools.renderCanvasData = renderCanvasData
},
scrollPage(offset) {
var index = this.stack.currentImageIdIndex + offset
if (index < 0) index = 0
else if (index >= this.stack.imageIds.length) {
index = this.stack.imageIds.length - 1
}
if (index !== this.stack.currentImageIdIndex) {
scroll(this.canvas, index)
}
},
toggleClipPlay() {
if (this.toolState.clipPlaying) {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
return
}
this.toolState.clipPlaying = true
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
cornerstoneTools.getToolState(
this.canvas,
'playClip'
).data[0].loop = false
},
setFps(fps) {
this.dicomInfo.fps = fps
},
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter
cornerstone.setViewport(this.canvas, viewport)
},
setWwwc(ww, wc) {
var viewport = cornerstone.getViewport(this.canvas)
viewport.voi.windowWidth = ww
viewport.voi.windowCenter = wc
cornerstone.setViewport(this.canvas, viewport)
},
toggleInvert() {
this.toolState.viewportInvert = !this.toolState.viewportInvert
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = this.toolState.viewportInvert
cornerstone.setViewport(this.canvas, viewport)
},
activateZoom() {
cornerstoneTools.addTool(cornerstoneTools.ZoomTool, {
configuration: {
invert: false,
preventZoomOutsideImage: false,
minScale: 0.1,
maxScale: 20.0
}
})
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
this.toolState.activeTool = 'zoom'
},
resetRotate() {
var viewport = cornerstone.getViewport(this.canvas)
viewport.hflip = false
viewport.vflip = false
viewport.rotation = 0
cornerstone.setViewport(this.canvas, viewport)
},
setRotate(hflip, vflip, angle) {
var viewport = cornerstone.getViewport(this.canvas)
if (hflip) viewport.hflip = !viewport.hflip
if (vflip) viewport.vflip = !viewport.vflip
if (angle !== 0) viewport.rotation += angle
cornerstone.setViewport(this.canvas, viewport)
},
saveImage() {
var uid = cornerstone.getImage(this.canvas).data.string('x00080018')
cornerstoneTools.SaveAs(this.canvas, `${uid}.png`)
},
fitToWindow() {
if (this.stack.seriesNumber) {
cornerstone.fitToWindow(this.canvas)
}
},
fitToImage() {
if (this.stack.seriesNumber) {
const enabledElement = cornerstone.getEnabledElement(this.canvas)
enabledElement.viewport.scale = 1
cornerstone.updateImage(this.canvas)
}
},
clearMeasurement() {
const viewport = cornerstone.getViewport(this.canvas)
if (viewport.colormap) {
this.resetRenderCanvase(this.canvas)
}
ToolStateManager.clear(this.canvas)
cornerstone.setViewport(
this.canvas,
cornerstone.getDefaultViewportForImage(
this.canvas,
cornerstone.getImage(this.canvas)
)
)
cornerstone.updateImage(this.canvas)
},
setToolPassive(toolName) {
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
},
setToolActive(toolName) {
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1
})
},
setAllToolsPassive() {
cornerstoneTools.store.state.tools.forEach((tool) => {
cornerstoneTools.setToolPassiveForElement(this.canvas, tool.name)
})
},
addTargetElement(synchronizer) {
synchronizer.addTarget(this.$refs.canvas)
},
removeTarget(synchronizer) {
synchronizer.removeTarget(this.$refs.canvas)
},
addSourceElement(synchronizer) {
synchronizer.addSource(this.$refs.canvas)
},
removeSource(synchronizer) {
synchronizer.removeSource(this.$refs.canvas)
},
activeReferenceLine(synchronizer) {
if (
!cornerstoneTools.getToolForElement(
this.canvas,
cornerstoneTools.ReferenceLinesTool
)
) {
cornerstoneTools.addToolForElement(
this.canvas,
cornerstoneTools.ReferenceLinesTool
)
}
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
synchronizationContext: synchronizer
})
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
// cornerstoneTools.setToolActive('Crosshairs', {
// mouseButtonMask: 1,
// synchronizationContext: synchronizer
// })
},
disabledReferenceLine(synchronizer) {
synchronizer.enabled = false
synchronizer.remove(this.canvas)
cornerstoneTools.setToolDisabledForElement(this.canvas, 'ReferenceLines')
// cornerstoneTools.setToolDisabledForElement(this.canvas, 'Crosshairs')
},
activeViewPortToolSync(synchronizer, toolName) {
synchronizer.add(this.canvas)
synchronizer.enabled = true
if (
!cornerstoneTools.getToolForElement(
this.canvas,
cornerstoneTools[`${toolName}Tool`]
)
) {
cornerstoneTools.addToolForElement(
this.canvas,
cornerstoneTools[`${toolName}Tool`]
)
}
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1,
synchronizationContext: synchronizer
})
},
disabledViewPortToolSync(synchronizer, toolName) {
synchronizer.enabled = false
synchronizer.remove(this.canvas)
cornerstoneTools.setToolDisabledForElement(this.canvas, toolName)
},
activeImageSync(synchronizer) {
synchronizer.add(this.$refs.canvas)
synchronizer.enabled = true
return false
},
disabledImageSync(synchronizer) {
synchronizer.remove(this.$refs.canvas)
synchronizer.enabled = false
return false
},
activeAnnotationSync(synchronizer) {
this.AnnotationSync = synchronizer
synchronizer.add(this.$refs.canvas)
synchronizer.enabled = true
},
disabledAnnotationSync(synchronizer) {
this.AnnotationSync = null
synchronizer.enabled = false
synchronizer.remove(this.$refs.canvas)
this.setAllToolsPassive()
},
onContextmenu(event) {
const colormapsList = cornerstone.colors.getColormapsList()
const colorItems = []
colorItems.push({
label: '默认值',
onClick: () => {
this.setColormap()
}
})
colormapsList.forEach(colormap => {
const item = {}
item.label = colormap.name
item.onClick = () => {
this.setColormap(colormap.id)
}
colorItems.push(item)
})
this.$contextmenu({
items: [
{
label: '移动',
divided: true,
onClick: () => {
this.setToolActive('Pan')
}
},
{
label: '缩放',
divided: true,
children: [
{
label: '自由缩放',
onClick: () => {
this.setToolActive('Zoom')
}
},
{
label: '适应图像',
onClick: () => {
this.fitToWindow()
}
},
{
label: '适应窗口',
onClick: () => {
this.fitToImage()
}
}
]
},
{
label: '透镜',
divided: true,
onClick: () => {
this.setToolActive('Magnify')
}
},
{
label: '旋转',
divided: true,
children: [
{
label: '默认值',
onClick: () => {
this.resetRotate()
}
},
{
label: '自由旋转',
onClick: () => {
this.setToolActive('Rotate')
}
},
{
label: '水平翻转',
onClick: () => {
this.setRotate(true, false, 0)
}
},
{
label: '垂直翻转',
onClick: () => {
this.setRotate(false, true, 0)
}
},
{
label: '左转90度',
onClick: () => {
this.setRotate(false, false, -90)
}
},
{
label: '右转90度',
onClick: () => {
this.setRotate(false, false, 90)
}
}
]
},
{
label: '测量',
divided: true,
minWidth: 0,
children: [
{
label: '探针',
onClick: () => {
this.setToolActive('Probe')
}
},
{
label: '长度测量',
onClick: () => {
this.setToolActive('Length')
}
},
{
label: '角度测量',
onClick: () => {
this.setToolActive('Angle')
}
},
{
label: 'Cobb测量',
onClick: () => {
this.setToolActive('CobbAngle')
}
},
{
label: '椭圆测量',
onClick: () => {
this.setToolActive('EllipticalRoi')
}
},
{
label: '矩形测量',
onClick: () => {
this.setToolActive('RectangleRoi')
}
},
{
label: '多边形标记',
onClick: () => {
this.setToolActive('FreehandRoi')
}
},
{
label: '十字线',
onClick: () => {
this.setToolActive('Bidirectional')
}
},
{
label: '文字标注',
onClick: () => {
this.setToolActive('ArrowAnnotate')
}
}
]
},
{
label: '调窗',
divided: true,
onClick: () => {
this.setToolActive('Wwwc')
}
},
{
label: '反色',
divided: true,
onClick: () => {
this.toggleInvert()
}
},
{
label: '伪彩',
children: colorItems
}
],
event,
// x: event.clientX,
// y: event.clientY,
customClass: 'class-a',
zIndex: 3,
minWidth: 100
})
return false
},
getToolSate() {
const toolROITypes = [
'Probe',
'EllipticalRoi',
'RectangleRoi',
'ArrowAnnotate',
'Length',
'CobbAngle',
'Angle',
'Bidirectional',
'FreehandRoi'
]
for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i]
const toolROIData = JSON.stringify(
cornerstoneTools.getToolState(this.canvas, toolROIType)
)
if (toolROIData !== undefined) {
this.allROIToolData[toolROITypes[i]] = JSON.parse(toolROIData)
}
}
},
clearToolState() {
ToolStateManager.clear(this.canvas)
cornerstone.updateImage(this.canvas)
},
removeLabel(item) {
const promise = scroll(this.canvas, item.data.imageIdIndex)
const scope = this
Promise.all([promise]).then(res => {
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
cornerstone.updateImage(scope.canvas)
})
}
}
}
</script>
<style scoped>
.info-series {
position: absolute;
left: 10px;
top: 10px;
text-align: left;
/* z-index: 1; */
}
.info-image {
position: absolute;
left: 10px;
bottom: 10px;
text-align: left;
/* z-index: 1; */
}
.info-subject {
position: absolute;
right: 10px;
top: 10px;
text-align: right;
/* z-index: 1; */
}
.load-indicator {
position: absolute;
left: 10px;
top: 10px;
text-align: left;
/* z-index: 1; */
}
.magnifyTool {
border: 2px solid #ffffff;
border-radius: 50%;
display: none;
cursor: none;
}
.menu__item {
display: block;
line-height: 20px;
text-align: center;
margin: 10px;
cursor: default;
}
.menu__item:hover {
color: #ff0000;
}
.menu {
height: auto;
width: auto;
position: absolute;
font-size: 14px;
text-align: left;
border-radius: 10px;
border: 1px solid #c21111;
background-color: #ffffff;
}
li:hover {
background-color: #e0e0e2;
color: white;
}
</style>