diff --git a/package-lock.json b/package-lock.json index 205955d4..d087fcb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21838,7 +21838,7 @@ }, "node_modules/unzipit": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unzipit/-/unzipit-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/unzipit/-/unzipit-2.0.3.tgz", "integrity": "sha512-e6+ftgSQRj9Hwf2mIWFwYIGqBoKV7AziG/d7pep2Uv3NCzHbhLO5d9dGRwpahVlnpQ7aQC+5kWk72dnWocdYyA==", "license": "MIT", "engines": { diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index 9240870a..cc6e4022 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -1057,7 +1057,7 @@ export default { this.trialId = this.$route.query.trialId this.readingTaskState = this.taskInfo.ReadingTaskState - if (!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder !== 0) { + if ((!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder === 1) || this.taskInfo.IsReadingTaskViewInOrder === 2) { this.rows = 1 this.cols = 2 this.activeViewportIndex = 1 @@ -1425,7 +1425,11 @@ export default { if (activeStudyIndex > -1 && activeSeriesIndex > -1) { seriesArr.map((i, index) => { - this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i) + const allowInitPrefetch = i.TaskInfo.IsCurrentTask === true && index === this.activeViewportIndex + this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i, false, { + limitInitPrefetch: allowInitPrefetch, + skipInitPrefetch: !allowInitPrefetch + }) }) const visitTaskId = this.visitTaskList[this.activeTaskIndex].VisitTaskId this.lastViewportTaskId = visitTaskId @@ -1436,7 +1440,7 @@ export default { // 初始化加载器 async initLoader() { await initLibraries() - cache.setMaxCacheSize(6 * 1024 * 1024 * 1024) + cache.setMaxCacheSize(4 * 1024 * 1024 * 1024) let renderingEngine = getRenderingEngine(renderingEngineId) if (!renderingEngine) { renderingEngine = new RenderingEngine(renderingEngineId) diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue index 14764a7a..03fe3a19 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue @@ -103,6 +103,7 @@ import { getPTImageIdInstanceMetadata } from '@/views/trials/trials-panel/readin import ptScalingMetaDataProvider from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/ptScalingMetaDataProvider' import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv' import { vec3 } from 'gl-matrix' +const InitPrefetchMaxBytes = 2 * 1024 * 1024 * 1024 export default { name: 'ImageViewport', props: { @@ -159,7 +160,8 @@ export default { wwwcIdx: 2, loading: false, forceFitToWindow: false, - resizeObserver: null + resizeObserver: null, + instanceFileSizeMap: {} } }, mounted() { @@ -210,6 +212,9 @@ export default { }, stackNewImage(e) { const { detail } = e + if (this.series?.Modality === 'PT' && detail?.image) { + this.cachePTMetadata([detail.image]) + } this.series.SliceIndex = detail.imageIdIndex this.sliderInfo.height = detail.imageIdIndex * 100 / (this.series.Stack.length - 1) const renderingEngine = getRenderingEngine(this.renderingEngineId) @@ -475,10 +480,57 @@ export default { viewport.render() }, - async prefetchMetadataInformation(imageIdsToPrefetch, modality) { + formatBytes(bytes) { + const size = Number(bytes) + if (!Number.isFinite(size) || size <= 0) return '0 B' + if (size >= 1024 * 1024 * 1024) return `${(size / 1024 / 1024 / 1024).toFixed(2)} GB` + if (size >= 1024 * 1024) return `${(size / 1024 / 1024).toFixed(2)} MB` + if (size >= 1024) return `${(size / 1024).toFixed(2)} KB` + return `${size} B` + }, + getImageInstanceId(imageId) { + try { + const rawImageId = imageId.includes(':') ? imageId.slice(imageId.indexOf(':') + 1) : imageId + const queryIndex = rawImageId.indexOf('?') + if (queryIndex === -1) return '' + const searchParams = new URLSearchParams(rawImageId.slice(queryIndex + 1)) + return searchParams.get('instanceId') || '' + } catch (error) { + return '' + } + }, + buildInstanceFileSizeMap() { + console.log('buildInstanceFileSizeMap') + const fileSizeMap = {} + const instanceList = this.series?.InstanceInfoList || [] + instanceList.forEach((item) => { + const instanceId = item?.Id + const fileSize = Number(item.FileSize) + if (instanceId !== undefined && instanceId !== null && fileSize > 0) { + fileSizeMap[String(instanceId)] = fileSize + } + }) + this.instanceFileSizeMap = fileSizeMap + }, + getImageFileSizeByImageId(imageId) { + const instanceId = this.getImageInstanceId(imageId) + if (!instanceId) return 0 + const fileSize = Number(this.instanceFileSizeMap[String(instanceId)] || 0) + return Number.isFinite(fileSize) && fileSize > 0 ? fileSize : 0 + }, + async prefetchMetadataInformation(imageIdsToPrefetch, modality, initialLoadedBytes = 0, enableInitPrefetchLimit = false) { + let totalLoadedBytes = Number.isFinite(initialLoadedBytes) && initialLoadedBytes > 0 ? initialLoadedBytes : 0 let taskPromises = [] for (let i = 0; i < imageIdsToPrefetch.length; i++) { + const imageId = imageIdsToPrefetch[i] + const nextImageBytes = this.getImageFileSizeByImageId(imageId) + // 初始化预取累计超过 2G 后停止,剩余切片在用户滚动时按需加载 + if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) { + console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)},停止继续预取。`) + break + } taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise) + totalLoadedBytes += nextImageBytes if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) { let res = await Promise.all(taskPromises) if (modality === 'PT') { @@ -500,7 +552,7 @@ export default { } }) }, - async setSeriesInfo(obj, isLocate = false) { + async setSeriesInfo(obj, isLocate = false, option = {}) { try { if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) { obj.SliceIndex = this.series.SliceIndex @@ -510,6 +562,7 @@ export default { if (isSameSeries && this.series?.Stack && obj.Stack[obj.SliceIndex] === this.series.Stack[this.series.SliceIndex]) return this.toggleClipPlay(false) this.series = { ...obj } + this.buildInstanceFileSizeMap() const renderingEngine = getRenderingEngine(this.renderingEngineId) const viewport = renderingEngine.getViewport(this.viewportId) let imageId = obj.ImageIds[obj.SliceIndex] ? obj.ImageIds[obj.SliceIndex] : obj.ImageIds[0] @@ -519,7 +572,12 @@ export default { if (obj.Modality === 'PT') { this.cachePTMetadata([image]) } - this.prefetchMetadataInformation(obj.ImageIds, obj.Modality) + const currentImageIndex = obj.ImageIds[obj.SliceIndex] ? obj.SliceIndex : 0 + const initialLoadedBytes = this.getImageFileSizeByImageId(imageId) + const prefetchImageIds = obj.ImageIds.filter((item, index) => index !== currentImageIndex) + if (option.skipInitPrefetch !== true) { + this.prefetchMetadataInformation(prefetchImageIds, obj.Modality, initialLoadedBytes, option.limitInitPrefetch === true) + } await viewport.setStack(this.series.Stack, obj.SliceIndex) this.syncViewportStaticState()