cornerstoneTool.js扩展工具

2,255 阅读3分钟

cornerstoneTool.js扩展工具

html

<div id="dicomImage" style="width: 512px;height: 512px;" oncontextmenu="return false" onmousedown="return false"></div>

script

<script src="https://unpkg.com/hammerjs@2.0.8/hammer.js"></script>
<script src="https://unpkg.com/cornerstone-core@2.3.0/dist/cornerstone.js"></script>
<script src="https://unpkg.com/cornerstone-math@0.1.9/dist/cornerstoneMath.min.js"></script>
<script src="https://unpkg.com/cornerstone-wado-image-loader@3.3.1/dist/cornerstoneWADOImageLoader.min.js"></script>
<script src="https://unpkg.com/cornerstone-web-image-loader@2.1.1/dist/cornerstoneWebImageLoader.min.js"></script>
<script src="https://unpkg.com/cornerstone-tools@5.1.5/dist/cornerstoneTools.js"></script>
<script src="https://unpkg.com/dicom-parser@1.8.7/dist/dicomParser.min.js"></script>
<script>
    cornerstoneTools.external.cornerstone = cornerstone;
    cornerstoneTools.external.Hammer = Hammer;
    cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
    cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
    cornerstoneWADOImageLoader.external.cornerstone = cornerstone;

    cornerstoneTools.init();

    let el = document.querySelector("#dicomImage");
    let imageId = "wadouri:http://127.0.0.1/download/dx123.dcm";
    cornerstone.enable(el);

    cornerstone.loadAndCacheImage(imageId).then(img => {
        cornerstone.displayImage(el, img);
    });
</script>

效果如下:

image.png

若是自己写工具的话,可以利用cornerstoneTools.import这个函数。

第一步

let BaseTool = cornerstoneTools.import("base/BaseAnnotationTool"); //自己写的工具要继承哪个基础工具,有三个可以继承的工具
/**
    base/BaseTool   官网General 差不多都是继承这个( 具体我也没有看完全部)
    base/BaseAnnotationTool  需要绘制一些图形可以继承这个 官网Annotation
    base/BaseBrushTool 官网Segmentation, 需要删除区域可以继承这个
*/

第二步

//自己的工具需要一个新的绘制环境,所以要获取一个函数
let getNewContext = cornerstoneTools.import("drawing/getNewContext");
//绘制板
let draw = cornerstoneTools.import("drawing/draw"); //这个不需要也行,这里函数只会简单的调用ctx.save();回调后调用 ctx.restore(); 建议还是要的,虽然官网没什么例子。
//绘制圆形
let drawCircle = cornerstoneTools.import("drawing/drawCircle");
//绘制线
let drawLine = cornerstoneTools.import("drawing/drawLine");

第三步

let _self = null; //保存this
//十字线
class CrosshairMpr extends BaseTool{
    constructor(props){
        let defaults = {
            name : "CrosshairMpr", //工具名称, 这个是必要的,没有这个,就不能使用自定义工具
            supportedInteractionTypes: ["Mouse"], //必要的, 支持那些操作方式, ["Mouse","Touch]
            configuration: { //配置,非必要
                lineWidth: 1, //自己定义的宽度
            }
        }
        const initialProps = Object.assign({}, defaults, props);
        super(initialProps); //继承
        //自己定义的属性
        this.x = 0; //开始x点
        this.y = 0; //开始y点
        this.dx = 0; //结束dx点
        this.dy = 0; //结束dy点
        this.cancleListenIndex = 0; //鼠标按下次数
        this.initConfiguration = initialProps; //混合后的配置
    }
    //可以利用cornerstoneTools.setTool[...]触发下面不同的函数 (PS:非必须)
    activeCallback(el, options){ //激活时触发
        this.width = el.offsetWidth;
        this.height = el.offsetHeight;
        this.x = this.width / 2;
        this.y = this.height / 2;
        this.dx = this.x;
        this.dy = this.y;
        if(!_self){
            _self = this;
        }
        this._renderCrosshair(getNewContext(el.firstElementChild),el,{},this.x,this.y);
    }
    disabledCallback(){} //禁用时触发
    passiveCallback(){} //被动时触发
    enabledCallback(){} //允许时触发
    preMouseDownCallback(e){ //鼠标点下触发事件
        /**
            这里比较狗, 鼠标按下提供了MouseEvent的事件对象,而鼠标移动却没有,只提供了e.detail.currentPoints.client ---> x : e.clientX, y : e.clientY  原本我是想用offset快点实现,后来没有提供又要client进行计算
        */
        e.preventDefault();
        e.stopPropagation();
        this.cancleListenIndex++;
        if(this.cancleListenIndex >= 2){
            this.cancleMouseMove();
            this.cancleListenIndex = 0;
            this.x = this.dx;
            this.y = this.dy;
            return true;
        }
        let event = e.detail.event;
        this.x = event.clientX - this.element.offsetLeft;
        this.y = event.clientY - this.element.offsetTop + 1;
        this.startMouse();
        return true;
    }
    postMouseDownCallback(e) { //阻止默认事件
        e.preventDefault();
        e.stopPropagation();
        return true;
    }
    renderToolData(e){ //触发图形改变或改变viewport都会触发这个函数渲染
        let event = e.detail;
  this._renderCrosshair(getNewContext(event.canvasContext.canvas),event.element,event,this.dx,this.dy);
    }
    _renderCrosshair(context,el,e,x,y){ //自定义函数
        let offset = 10;
        let high = (this.height / 2);
        let wide = (this.width / 2);
        let dx = (wide - x);
        let dy = (high - y);
        draw(context,ctx=>{//ctx 也是context
            //drawLine有五个参数 context, element,start,end,options
            /**
                context 上下文环境
                element enabled元素
                start   开始坐标点{x : 0, y : 0}
                end     结束坐标点{x : 0,y : 0}
                options 对象 ---> color,lineWidth,lineDash,fill
                args[5] 这个参数是用来决定是否用 cornerstone.pixelToCanvas获取开始和结束点,如果不传参就是 pixel, 传入start和end将无效, 传有效值将使用start和end
            */
            drawLine(ctx,el,{x : x,y : 0},{x : x,y : high - offset - dy},{
                color : "orange",
                lineWidth : this._configuration.lineWidth || 1
            },"canvas");
            drawLine(ctx,el,{x : x,y : high + offset - dy},{x : x,y : this.height},{
                color : "#38f",
                lineWidth : this._configuration.lineWidth || 1
            },"canvas");

            drawLine(ctx,el,{x : 0,y : y},{x : wide - offset - dx,y : y},{
                color : "#f20",
                lineWidth : this._configuration.lineWidth || 1
            },"canvas");
            drawLine(ctx,el,{x : wide + offset - dx,y : y},{x : this.width,y : y},{
                color : "green",
                lineWidth : this._configuration.lineWidth || 1
            },"canvas");
            //这个也是同理
            drawCircle(ctx,el,{
                x : x,
                y : y
            },offset - 4,{
                color : "purple",
                lineWidth : this._configuration.lineWidth || 1
            },"canvas");
        });
    }
    startMouse(){ //自定义函数
        this.element.addEventListener("cornerstonetoolsmousemove",listenMouse);
    }
    cancleMouseMove(){//自定义函数
        this.element.removeEventListener("cornerstonetoolsmousemove",listenMouse);;
    }
    _resetRender(){//自定义函数, 触发重渲染
        let viewport = cornerstone.getViewport(this.element);
        cornerstone.setViewport(this.element,viewport);
    }
}
//监听鼠标移动
function listenMouse(e){
    let event = e.detail;
    let client = event.currentPoints.client;
    _self.dx = _self.x + (client.x - this.offsetLeft - _self.x);
    _self.dy = _self.y + (client.y - this.offsetTop + 1 - _self.y);
    _self._resetRender();
}

cornerstoneTools.addTool(CrosshairMpr); //添加工具
cornerstoneTools.setToolActive("CrosshairMpr",{ //激活工具
    mouseButtonMask : 1
});

效果如下:

image.png


切记不要自己利用canvas清除API清除 ctx.clearRect(0,0,width,height)这会有点危险。
我看官网没有这个API,但是OHIF.org这个demo中的2D MPR实现有,刚好项目也需要这个功能,就自己实现了。至于如果你想像MPR那样联动位置的话,就要用 vtk.js实现了。