影像云胶片:Dicom纯js的三维重建影像阅片器
影像云胶片主要:通过浏览器浏览Dicom影像阅片,通过多终端设备(如电脑、平板、手机),为医生、患者及其他授权人员提供随时随地的影像调阅功能,打破时间和空间限制。同时,支持影像数据在不同医疗机构之间的安全共享,促进医疗协作与远程会诊的开展。主要功能:
- 支持标准DIcom影像的2D浏览,预设窗位,伪彩,序列间,序列内多种布局方式。
- 影像处理,提供影像翻图、缩放、移动、透镜、反相、旋转、截图等操作
- 影像测量,提供箭头、直线、十字架、角度、Cobb、心胸比、椭圆、矩形、勾画,橡皮擦、CT值等数据的测量
- 支持影像的三维重建,包括多平面重建,容积漫游技术(VR)、最大/小密度投影等功能
- 支持影像胶片打印功能,包括DR,CT等胶片自定义布局打印
- 支持平板,手机等移动设备的影像浏览
总体架构:BS(浏览器/服务器)架构
前端:基于HTML5、WebGL技术开发,无需安装插件,支持PC浏览器、手机H5、微信小程序、支付宝小程序等多种终端。
后端:后端居于fo-dicom组件进行开发,对dicom文件访问授权服务,访问MD5签名验证等身份验证。
核心技术栈
影像渲染引擎:使用 VTK.js 等开源框架,实现基于WebGL的高性能二维、三维影像渲染。
DICOM服务:自研或使用开源组件(如fo-dicom)实现DICOM文件的接收(C-Store)、查询(C-Find)、检索(C-Move)等服务。
主要代码示例
1:前端采用有vue3+element-ui进行开发,检查屏幕大小,以便适应不同的屏幕
<script setup>
import LeftSeriesMobile from './views/LeftSeriesMobile.vue'
import CenterImage from './views/CenterImage.vue'
import RightToolMobile from './views/RightToolMobile.vue'
import { ElMessageBox } from 'element-plus' // 注册resize事件监听器 调整laytou大小
window.addEventListener('resize', function (evt) {
if (window.innerWidth > 768) {
ElMessageBox.alert("系统检测到你在阅片期间对浏览器进行了缩放。为防止图像显示及测量工作不精确,强烈建议您重新打开影像!");
}
}); </script> <template>
<div class="divRightMb">
<RightToolMobile />
</div>
<div class="divCenterMb" id="divCenter">
<CenterImage />
</div>
<div class="divLeftMb">
<LeftSeriesMobile />
</div>
</template> <style scoped></style>
最终在PC端和手机端效果图如下:


2:使用VTK.js 等开源框架,实现基于WebGL的高性能二维、三维影像渲染。
//初始化一个视窗
function ViewGridChange_LoadDicom_OneView(layout, element, showSeries) {
//注册事件
element.addEventListener(IMAGE_RENDERED, IMAGE_RENDERED_CallBack); layout.ShowSeries = showSeries;//当前窗体显示的序列对象
element.ShowSeries = showSeries;//当前窗体显示的序列对象
let viewport;
if (showSeries.SeriesItemType == 1) {
viewport = {
viewportId: layout.viewportId,
type: showSeries.viewType,
element: element
}
element.addEventListener(STACK_NEW_IMAGE, STACK_NEW_IMAGE_CallBack);
} else {
viewport = {
viewportId: layout.viewportId,
type: showSeries.viewType,
element: element,
defaultOptions: {
orientation: showSeries.orientation
},
}
element.addEventListener(VOLUME_NEW_IMAGE, VOLUME_NEW_IMAGE_CallBack);
}
return viewport;
}
3:实现三维十字准线工具的功能
function InitCrosshairsTool(renderingEngineId, toolGroupObj, viewLayoutArray) {
if (!VailSign()) return;
_renderingEngineId = renderingEngineId;
viewportColors = new Array();
viewportReferenceLineControllable = new Array();
viewportReferenceLineDraggableRotatable = new Array();
viewportReferenceLineSlabThicknessControlsOn = new Array();
for (let i = 0; i < viewLayoutArray.length; i++) {
const curLayout = viewLayoutArray[i];
if (!curLayout.ShowSeries) { continue; }
if (curLayout.IsCrosshairs != true) { continue; }
let isAdd = false;
if (curLayout.ShowSeries.SeriesItemType == 2) {
viewportColors[curLayout.viewportId] = 'rgb(200, 0, 0)';
isAdd = true;
} else if (curLayout.ShowSeries.SeriesItemType == 3) {
viewportColors[curLayout.viewportId] = 'rgb(200, 200, 0)';
isAdd = true;
} else if (curLayout.ShowSeries.SeriesItemType == 4) {
viewportColors[curLayout.viewportId] = 'rgb(0, 200, 0)';
isAdd = true;
}
if (isAdd == true) {
viewportReferenceLineControllable.push(curLayout.viewportId);
viewportReferenceLineDraggableRotatable.push(curLayout.viewportId);
viewportReferenceLineSlabThicknessControlsOn.push(curLayout.viewportId);
}
}
if (toolGroupObjIsAdd == false) {
cornerstoneTools.addTool(CrosshairsTool);
const isMobile = window.matchMedia('(any-pointer:coarse)').matches;
toolGroupObj.toolGroup.addTool(CrosshairsTool.toolName, {
getReferenceLineColor,
getReferenceLineControllable,
getReferenceLineDraggableRotatable,
getReferenceLineSlabThicknessControlsOn,
mobile: {
enabled: isMobile,
opacity: 0.8,
handleRadius: 9,
},
});
toolGroupObjIsAdd = true;
}
if (viewportReferenceLineControllable.length > 1) {
//toolGroupObj.toolGroup.setToolPassive(CrosshairsTool.toolName);//激活 十字准线
toolGroupObj.SetMouseLeftTool(CrosshairsTool.toolName);//初始化默认激活左键为 3D探针
}
}
三维重建效果如图所示:


4:胶片打印布局设置相关代码
//设置中间图像大小
let curGolb_PaperSize = "";//当前纸张大小
let curGolb_Direction = "";//当前方向
let curGolb_ShowScale = "1";//当前显示比例
function CenterSetImageSize(paperSize, direction, showScale, isPrint) {
SetImageSizeisPrint = isPrint;
if (isEmpty(paperSize)) paperSize = curGolb_PaperSize;
if (isEmpty(direction)) direction = curGolb_Direction;
if (isEmpty(showScale)) showScale = curGolb_ShowScale;
if (curGolb_PaperSize == paperSize && curGolb_Direction == direction && curGolb_ShowScale == showScale) {
return;
}
curGolb_PaperSize = paperSize;
curGolb_Direction = direction;
curGolb_ShowScale = showScale; //根据纸张大写设置图像大小
let paperObj = null;
for (let i = 0; i < PaperSizeArys.length; i++) {
const item = PaperSizeArys[i];
if (item.value == paperSize) {
paperObj = item;
break;
}
}
if (!paperObj) return; //设置图像大小
let imgWidth = 0;
let imgHeight = 0;
if (direction == "0") {//横向
imgWidth = paperObj.ImgHeight;
imgHeight = paperObj.ImgWidth;
} else {//纵向
imgWidth = paperObj.ImgWidth;
imgHeight = paperObj.ImgHeight;
}
let imgWidthPx = imgWidth * 96;
let imgHeightPx = imgHeight * 96; let scaleFloat = 1;
if (showScale == "-2") {//自适应宽高
const rect = divImgElemet.parentElement.getBoundingClientRect();
//如果宽或者高 任意一个大于了外层容器 则进行缩放
if (imgWidthPx > rect.width || imgHeightPx > rect.height) {
//如果宽超出的范围更大,则以宽为基准进行缩放
if (imgWidthPx - rect.width >= imgHeightPx - rect.height) {
scaleFloat = rect.width / imgWidthPx;
} else {
//以高位基准进行缩放
scaleFloat = rect.height / imgHeightPx;
}
scaleFloat = scaleFloat * 0.98;//自适应时设置图像显示比例98%,防止出现滚动条
}
} else if (showScale == "-1") {//自适应宽
const rect = divImgElemet.parentElement.getBoundingClientRect();
//如果宽或者高 任意一个大于了外层容器 则进行缩放
if (imgWidthPx > rect.width) {
//如果宽超出的范围更大,则以宽为基准进行缩放
scaleFloat = rect.width / imgWidthPx * 0.98;//自适应时设置图像显示比例98%,防止出现滚动条
}
} else {
scaleFloat = parseFloat(showScale);
}
imgWidth = numToFixed(imgWidth * scaleFloat, 0) + "in";
imgHeight = numToFixed(imgHeight * scaleFloat, 0) + "in"; if (imgWidth == divImgElemet.style.width && imgHeight == divImgElemet.style.height) {
//图像大小不变,如果是打印则直接执行打印
if (SetImageSizeisPrint == true) {
PrintCurrentPage(false);
}
} else {
divImgElemet.style.width = imgWidth;
divImgElemet.style.height = imgHeight; //设置图像上字体大小
let dfontSieze = 11;
if (!isEmpty(PrintConfigObj.FontSize)) {
dfontSieze = parseInt(PrintConfigObj.FontSize);
}
const setFontSize = numToFixed(dfontSieze * scaleFloat, 2) + "px";//标注字体为11px,乘以缩放比例
divImgElemet.style.fontSize = setFontSize; //获取元素像素大小
document.querySelector("#spShowImgSizePx").innerHTML = divImgElemet.scrollWidth + "×" + divImgElemet.scrollHeight;
}
}
胶片打印布局效果如图

技术交流沟通联系QQ:343798739;469116292
影像云胶片:Dicom纯js的三维重建影像阅片器的更多相关文章
- Highcharts纯js图表库,以后可以跟客户说,你跟阿里云ECS用的图表库是同款
Highcharts是一款纯javascript编写的图表库,能够很简便的在Web网站或Web应用中添加交互性的图表,Highcharts目前支持直线图.曲线图.面积图.柱状图.饼图.散点图等多达18 ...
- 纯JS实现俄罗斯方块,打造属于你的游戏帝国
纯JS俄罗斯方块,打造属于你的游戏帝国. 本文原始作者博客 http://www.cnblogs.com/toutou 俄罗斯方块(Tetris, 俄文:Тетрис)是一款电视游戏机和掌上游戏机游戏 ...
- h5移动版云胶片系统
h5移动版云胶片系统. 最近开了一个医疗用的云胶片,可以对图片放大.图片缩小,图片移动,图片调窗,图片切换,图片播放,图片变灰等等功能.如下图:
- F2工作流引擎之-纯JS Web在线可拖拽的流程设计器(八)
Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回.传阅.转交,都可以非常方便快捷地实现,管理员 ...
- [原创]纯JS实现网页中多选复选框checkbox和单选radio的美化效果
图片素材: 最终效果图: <html><title> 纯JS实现网页中多选复选框checkbox和单选radio的美化效果</title><head>& ...
- 纯js异步无刷新请求(只支持IE)
纯js异步无刷新请求 下载地址:http://pan.baidu.com/s/1slakL1F 所以因为非IE浏览器都禁止跨域请求,所以以只支持IE. <HTML> <!-- 乱码( ...
- [分享黑科技]纯js突破localstorage存储上线,远程抓取图片,并转码base64保存本地,最终实现整个网站所有静态资源离线到用户手机效果却不依赖浏览器的缓存机制,单页应用最新黑科技
好久没有写博客了,想到2年前答应要放出源代码的也没放出来,最近终于有空先把纯js实现无限空间大小的本地存储的功能开源了,项目地址https://github.com/xueduany/localsto ...
- 纯js实现复制到剪贴板功能
在网页上复制文本到剪切板,一般是使用JS+Flash结合的方法,网上有很多相关文章介绍.随着 HTML5 技术的发展,Flash 已经在很多场合不适用了,甚至被屏蔽.本文介绍的一款JS插件,实现了纯J ...
- baguetteBox.js响应式画廊插件(纯JS)
baguetteBox.js baguetteBox.js 是一个简单和易于使用lightbox纯JavaScript脚本,拥有图像放大缩小并带有相应的CSS3过度,并能在触摸屏等设备上完美展示. D ...
- 纯js和纯css+html制作的手风琴的效果
一:纯css+html的手风琴效果 这种用css写的手风琴比较简单,主要是应用到css中的,transition属性. 代码如下: <!DOCTYPE HTML> <html> ...
随机推荐
- C#、.Net通过HttpWebRequest请求WebService接口
C#调用WebService有三种方式,静态调用.动态调用.Http访问,今天我们要实现的HttpWebRequest来调用WebService. 首先,需要在HttpWebRequest上面加一个S ...
- java常见知识点
工作中发现,很多同事,尤其是新同事,对一些基础知识点不熟悉,在此做梳理和总结,便于后期沟通. 一.jvm预热 https://www.cnblogs.com/crazymakercircle/p/17 ...
- Linux按行读取文件内容
方法1:while循环中执行效率最高,最常用的方法. function while_read_LINE_bottm(){ While read LINE do echo $LINE done < ...
- vue框架图片懒记载插件 vue-lazyload
如果一个网页有上百张图片,那么肯定需要用懒加载的方式来加载图片,比如用户滚动了,图片在逐渐加载出来,避免一次性加载太多图片. vue-lazyload官方demo. vue-lazdload npm ...
- Microsoft 推出 .NET 10 RC 1
北京时间2025.09.09 晚上 Microsoft 宣布推出 .NET 10 RC 1,这是.NET 10的一个重要里程碑,使开发人员能够自信地在其生产应用程序中使用最新版本.此RC版本包括跨各个 ...
- 让AI“上天”“入地” 百度打开未来世界
将大模型和生成式AI送到普通人手里,让大模型触手可得. 从ChatGPT问世至今,已经过去2年.大模型和生成式AI带给人们的,除了无限畅想,还有理想与现实的差距. 即便AI手机.AI PC.AI汽车相 ...
- 达成设计卓越:全面解析 IC 设计中的验证之道
在集成电路(IC)设计流程中,验证(Verification) 是确保设计符合规格.功能正确的关键环节.本文以简洁明快的风格,深入探讨验证的重要性.常用方法与实践经验,帮助设计师构建高效可靠的验证流程 ...
- 【有源码】医院挂号系统+SpringBoot+VUE+前后端分离
学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:医院挂号系统. 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 管理后台-截图 前 ...
- 较难理解的字符串查找算法KMP
时间复杂度O(n)的子串查找算法. 经典实例 主字符串(s):abcabcabd 模式串(t):abcabd 比较次数 主字符串 模式串 备注 一 abcabcabd a ...
- Unix哲学思想
一切都是一个文件(包括硬件) 小型,单一用途的程序 链接程序,共同完成复杂的任务(shell脚本) 避免令人困惑的用户界面 配置数据存储在文本中