首页 前端知识 vue医学图像处理包cornerstone.js基本使用

vue医学图像处理包cornerstone.js基本使用

2024-05-13 10:05:07 前端知识 前端哥 887 326 我要收藏

vue医学图像处理包cornerstone.js基本使用

本文为编辑2D图像的
先上参考链接:
cornerstonejs/cornerstoneTools: A framework for tools built on top of Cornerstone. (github.com)

Introduction · cornerstone-tools (cornerstonejs.org)

Cornerstone Tools: Examples (cornerstonejs.org)

Cornerstone Tools: API Docs (cornerstonejs.org)

Modules · cornerstone-tools (cornerstonejs.org)

cornerstoneTools/docs/latest/modules/segmentation.md at master · cornerstonejs/cornerstoneTools (github.com)

🎉 🎉 🎉 CornerstoneTools 4.0 🎉 🎉 🎉 · Issue #1061 · cornerstonejs/cornerstoneTools (github.com)

安装依赖

npm install --save cornerstone-core cornerstone-math cornerstone-tools hammerjs cornerstone-web-image-loader

PS:这里应该是没落下东西,以防万一,这里写一下package.json

  "dependencies": {
    "bootstrap": "^5.3.2",
    "bootstrap-vue-next": "^0.14.10",
    "cornerstone-core": "^2.6.1",
    "cornerstone-math": "^0.1.10",
    "cornerstone-tools": "^6.0.10",
    "cornerstone-web-image-loader": "^2.1.1",
    "hammerjs": "^2.0.8",
    "nprogress": "^0.2.0",
    "vue": "^3.3.4",
    "vue-router": "^4.2.5"
  },

code

初始化

首先肯定是在html也就是<template>标签里面写好自己要写入的位置:

<div id="cornerstone"/>

<script>标签里面引入需要的依赖

// cornerstone tool
import cornerstone from 'cornerstone-core';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
import Hammer from 'hammerjs';
import cornerstoneWebImageLoader from "cornerstone-web-image-loader"
// external
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneWebImageLoader.external.cornerstone = cornerstone;
// init
cornerstoneTools.init(
    {
        /**
         * Most tools have an associated canvas or SVG cursor. Enabling this flag
         * causes the cursor to be shown when the tool is active, bound to left
         * click, and the user is hovering the enabledElement.
         */
        showSVGCursors: true,
        outlineWidth: 2
    },
    {
    	// 这里的配置项用于裁剪功能
        moduleName: 'segmentation',
        configuration: {
            outlineWidth: 2
        }
    }
);
// segmentation
const { setters, getters } = cornerstoneTools.getModule('segmentation');

Module属性见(后面导出裁剪后的图片要用):
Modules · cornerstone-tools (cornerstonejs.org)
下面使用的vue3,vue2也差不太多,首先看mounted里面的初始化信息(代码后面有简介):

export default defineComponent({
    mounted() {
        this.imageUrl = '你的图片地址';
        this.tools = {
            PanTool: cornerstoneTools.PanTool,
            LengthTool: cornerstoneTools.LengthTool,
            MagnifyTool: cornerstoneTools.MagnifyTool,
            AngleTool: cornerstoneTools.AngleTool,
            FreehandScissorsTool: cornerstoneTools.FreehandScissorsTool,
            CircleScissorsTool: cornerstoneTools.CircleScissorsTool,
            RectangleScissorsTool: cornerstoneTools.RectangleScissorsTool
        }
        // Make sure we have at least one element Enabled
        this.element = document.querySelector('#cornerstone');
        cornerstone.enable(
            this.element,
            {
                colormap: "" // 玄学的对象,留着吧
            }
        );
        const viewport = cornerstone.getViewport(this.element);
        /*
        	简单列一下属性,我这里没用他们
            hflip : 水平旋转
            vflip : 垂直旋转
            invert : 颜色反转
            rotation : 旋转角度
            scale : 缩放
            translation : 位移
            voi : 切片
            windowWidth : 窗口宽度
            windowCenter : 窗口中心
        */
        const imageIds = [
            this.imageUrl
        ];
        // stack,使用裁剪功能需要的东西,Segmentation required
        const stack = {
            currentImageIdIndex: 0,
            imageIds: imageIds,
        };
        cornerstone.loadAndCacheImage(imageIds[0]).then((image) => {
        	// 图片的大小
            this.imgShape = [image.width, image.height];
            // 图片的数据
            this.imgData = image.getPixelData();
            cornerstoneTools.addStackStateManager(this.element, ['stack']); // Segmentation required
            cornerstoneTools.addToolState(this.element, 'stack', stack); // Segmentation required
            cornerstone.displayImage(this.element, image);
            cornerstone.setViewport(this.element, viewport);
            // default add zoom tool to ALL currently Enabled elements
            cornerstoneTools.addTool(cornerstoneTools.ZoomMouseWheelTool);
            cornerstoneTools.setToolActive('ZoomMouseWheel', { mouseButtonMask: 1 })
        });
    },
    data() {
        return {
            imageUrl: '',
            element: null,
            tools: [],
            selected: '',
            name: {
                PanTool: 'Pan',
                LengthTool: 'Length',
                MagnifyTool: 'Magnify',
                AngleTool: 'Angle',
                FreehandScissorsTool: 'FreehandScissors',
                CircleScissorsTool: 'CircleScissors',
                RectangleScissorsTool: 'RectangleScissors'
            },
            imgData: [],
            imgShape: []
        }
    },
...

这里的this.toolsthis.name里面的数据可以参考:
Cornerstone Tools: Examples (cornerstonejs.org)

PS:截止本人写博客的时间2023.12.13,这个页面还是没有完善

methods

接下来看methods里面的方法

激活工具
activaTool(tool) {
    // Adds tool to ALL currently Enabled elements
    this.selected = tool;
    /*
        下面是将裁剪工具设置为FILL_INSIDE,也就是内部填充
        还有属性:
            FILL_OUTSIDE: 外部填充,
            ERASE_OUTSIDE: 擦除外部,
            ERASE_INSIDE: 擦除内部,
    */
    if(tool.includes('Scissors')) {
        cornerstoneTools.addTool(this.tools[tool], { defaultStrategy: "FILL_INSIDE" });
    }else {
        cornerstoneTools.addTool(this.tools[tool]);
    }
    cornerstoneTools.setToolEnabled(this.name[tool], { mouseButtonMask: 1 });
    cornerstoneTools.setToolActive(this.name[tool], { mouseButtonMask: 1 })
},

使用的时候传入工具名称就好,例如在this.tool里面的属性LengthTool

activaTool('LengthTool')
清空所有基本工具

这个方法清空所有工具,除了scissors

clearAllTool() {
    for(let k in this.name) {
        cornerstoneTools.clearToolState(this.element, this.name[k]);
    }
    // 写了他才能让界面也更新清除了tool的视图
    cornerstone.updateImage(this.element);
    // reset viewport将视图重置位置
    cornerstone.reset(this.element);
},
直接保存视图,所见所得
saveRes() {
    // 直接保存viewport,所见所得
    cornerstoneTools.SaveAs(this.element, “你的文件名” + Date.now() + ".jpg");
},

图片后缀是jpg还是png都行

※※※保存Scissors工具覆盖的区域
saveCrop() {
    const labelmap2D = getters.labelmap2D(this.element).labelmap2D;
    // 获取像素数据
    const pixelData = labelmap2D.pixelData;
    // 使用 filter 过滤出非0值
    let nonZeroValues = pixelData.filter(element => element !== 0);
    // 非0 的数量不为0(有mask)
    if(nonZeroValues.length) {
	    // 初始化边界值为图像覆盖区域的的宽度和高度
	    let minX = this.imgShape[0];
	    let minY = this.imgShape[1];
	    let maxX = 0;
	    let maxY = 0;
	    // 创建一个新的Uint8ClampedArray,用于存储标记区数据
	    var roi = new Uint8ClampedArray(this.imgData.length);
	    // 遍历 pixelData
	    for (let i = 0; i < pixelData.length; i++) {
	        // 如果像素值不为0
	        if (pixelData[i] !== 0) {
	            // 计算像素的坐标
	            const x = i % this.imgShape[0];
	            const y = Math.floor(i / this.imgShape[0]);
	            // 更新边界值
	            minX = Math.min(minX, x);
	            minY = Math.min(minY, y);
	            maxX = Math.max(maxX, x);
	            maxY = Math.max(maxY, y);
	            // 如果mask值非零,则将对应位置的data值复制到roi数组
	            roi[i * 4] = this.imgData[i * 4];         // R component
	            roi[i * 4 + 1] = this.imgData[i * 4 + 1]; // G component
	            roi[i * 4 + 2] = this.imgData[i * 4 + 2]; // B component
	            roi[i * 4 + 3] = this.imgData[i * 4 + 3]; // Alpha component
	        }else {
	            // 如果mask值为零,则将对应位置的roi值设为0
	            roi[i * 4] = 0;
	            roi[i * 4 + 1] = 0;
	            roi[i * 4 + 2] = 0;
	            roi[i * 4 + 3] = 0; // 设置 alpha 值为 0,表示完全透明
	        }
	    }
	    // 计算覆盖区域的长度和宽度
	    const coveredWidth = maxX - minX + 1;
	    const coveredHeight = maxY - minY + 1;
	    // 下面是导出标记区域
	    // 创建一个Canvas元素
	    const canvas = document.createElement('canvas');
	    const ctx = canvas.getContext('2d');
	    // 设置Canvas的宽度和高度
	    canvas.width = coveredWidth;
	    canvas.height = coveredHeight;
	    // 创建ImageData对象,用于设置图像数据
	    const imageData = ctx.createImageData(coveredWidth, coveredHeight);
	    // 将roi数据复制到ImageData对象中,但是要根据有效区域进行偏移
	    for (let i = 0; i < coveredHeight; i++) {
	        for (let j = 0; j < coveredWidth; j++) {
	            const sourceIndex = ((minY + i) * this.imgShape[0] + minX + j) * 4;
	            const targetIndex = (i * coveredWidth + j) * 4;
	            for (let k = 0; k < 4; k++) {
	                imageData.data[targetIndex + k] = roi[sourceIndex + k];
	            }
	        }
	    }
	    // 将ImageData对象绘制到Canvas上
	    ctx.putImageData(imageData, 0, 0);
	    // 将Canvas转换为DataURL
	    const dataURL = canvas.toDataURL('image/png');
	    // 创建一个虚拟链接
	    const a = document.createElement('a');
	    // 设置链接的href属性为DataURL
	    a.href = dataURL;
	    // 设置链接的下载属性和文件名
	    let fileName = “自己设置自己的文件名字”;
	    // 文件名加上随机值
	    a.download = fileName + Math.floor(Math.random() * new Date().getTime()) + '.png';
	    // 模拟点击链接以触发下载
	    a.click();
    }else {
        alert('请选择一个区域');
    }
},

最后保存的是png后缀的,只有标记区域,就算是不规则形状也是
其余区域透明

undo框选

PS:框选不能用我的clearAllTool函数清除

myUndo() {
    setters.undo(this.element);
},
redo框选
myRedo() {
    setters.redo(this.element);
}
清除所有框选区域
clearMask() {
    const labelmap2D = getters.labelmap2D(this.element).labelmap2D;
    const pixelData = labelmap2D.pixelData;
    pixelData.fill(0);
    setters.updateSegmentsOnLabelmap2D(labelmap2D)
    // 写了他才能让界面也更新清除了mask的视图
    cornerstone.updateImage(this.element);
},
还原所有操作

这里需要调用前面写好的两个函数了就

resetAll() {
    this.clearAllTool();
    this.clearMask();
}
转载请注明出处或者链接地址:https://www.qianduange.cn//article/8457.html
标签
图像处理
评论
会员中心 联系我 留言建议 回顶部
复制成功!