基于cornerstone.js的人工智能标注项目

2,814 阅读5分钟

简单介绍cornerstone.js

人工智能医疗方向的研究是基于大量的医疗影像进行深度学习训练,训练的前提也就是标注,医疗影像有各种格式的图像,但是常用的是dicom图像,后缀.dcm,这种格式web是不支持的,cornerstone.js的作用是将dcm绘制到指定的canvas容器上。cornerstone.js只能应用于服务环境下。

cornerstone.js的使用就不做介绍了,注重介绍一下其中的坐标转换。

坐标转换

之所以将dcm数据绘制到web上,是为了便于标注,通过对病变位置的标注,来进行学习。

上图第一张是原始肺CT图片,中间是缩放平以后的图片,最后是缩放平移后选取的图片(这是一张肺部ct图,图中红色的圈是对结节的手动标注)

坐标转换的问题来了:第三张图中,红色圆圈的实现方式很简单,就是在canvas上绘制红色的点,然后连起来,简单的一个画笔功能。那么问题是,当图像缩放平移后,如何得知画笔相对于图像的坐标?

设么意思呢?:请理解第三张图,假设图中红色的点坐标是(160,320),---图中红圈是由n个点组成的,我们假设他一个点的坐标,只要求出一个点的坐标转换方法,其他的也一样。那么无论图片怎么动,这个点的坐标都是(160,320),他是相当Canvas的坐标,(点是绘制在canvas上,图片也是绘制在canvas上,无论图片怎么移动,点是不会动的。)那么当用户在放大平移这张图片以后,找到了一个病变的地方,圈出来,那么现在想要获取这个红圈在原图上的位置,就要进行坐标转换。
如果不理解换一种思路,在你的电脑上查看一张图片或者照片,现在拖动照片窗口的位置,然后放大照片或缩小照片,现在拿左手手指点住显示屏,点中你要关注的点,不要拿开,右手使用鼠标把敞口和图片还原到原来的位置大小,那么你现在的手指还在你点中的位置上么?这就是坐标转换,我们的需求是医生在查看Dicom图像,手动标注图像的可疑位置,然后我们还原红圈通过算法检测医生标注的是否正确,这就是坐标转换的初衷:

直接给出公式:(pos.x-(imgX+(cv1.width/2-img.width*scale/2)))/scale

代码:

else  if(3 == e.which)
  {    //判断是否为右键
     lastX = e.clientX;
     lastY = e.clientY;
     this.$refs.cv.mousemove(function (e) {
      deltaX = e.clientX - lastX,
      deltaY = e.clientY - lastY;
      lastX = e.clientX;
      lastY = e.clientY;
      
      viewport = cornerstone.getViewport(element);
      viewport.translation.x += (deltaX / viewport.scale);
      viewport.translation.y += (deltaY / viewport.scale);
      // viewport.translation.x += deltaX ;
      viewportX=viewport.translation.x;

      // viewport.translation.y += deltaY ;
      viewportY=viewport.translation.y;
      console.log(viewportX);
      console.log(viewportY);
      cornerstone.setViewport(element, viewport);
  });

当鼠标右键点击时,默认对图片操作平移功能。

  注意这个变量 viewport.translation.x,这就是他提供的默认的图像移动属性,通过+=鼠标移动量,从而对让图像平移,注意代码注释的部分,最开始我的实现方式是:

// viewport.translation.x += deltaX,这才是真正正确的操作方法

 平时在canvas移动图像的步骤是:1.获取鼠标移动量  2.计算图片的起始位置+鼠标移动 3.清空画布,然后根据2算出的位置重新绘制drawimage(),现在这个库给出了translation的属性,无需上述复杂操作(也操作不了,无法获取画布),直接赋值。但是按照常规的算法// viewport.translation.x += deltaX,图片完全可以移动,没有问题,鼠标和图片的相对位置可就不一样了。  

截图的时候截不到鼠标图标,我就根据位置自己画了一个,红色的箭头(两次我都是点在图片上,然后进行平移)

左图是使用viewport.translation.x += (deltaX / viewport.scale),可以看到鼠标的位置还是在图片上,相对位置并没有改变。

右图是使用viewport.translation.x += deltaX,鼠标我开始也是点在图片上向右拖动的,但是图片向右移动量大于鼠标移动量,造成了图中的现象,图片走的多了。此时,图片的移动量,根本就不是鼠标的移动量,坐标转换种的imgX鼠标移动量对应的不是图片移动的距离,先跨一步,然后回来说这个移动量的问题--@1。

通过使用 viewport.translation.x += (deltaX / viewport.scale),那么对应的坐标转换公式:

(pos.x-(viewportXviewport_Scale+(cv1.width/2-img.widthviewport_Scale/2)))/viewport_Scale

公式中viewportX=viewport.translation.x,我自定义了变量赋值,就是上文提到的,我在这里直接使用viewport.translation.x,就报错了,无法获取,然后我用其他变量接受了一下。这不是重点

viewportX*viewport_Scale 这个才是重点    

上文中,/scale,下面公式又*scale,不是没有改变吗?

因为,原公式是没有*scale的 ,转换公式这里只是imgX

解释:公式的大体思路是:鼠标拖动图片向右移动了50px,那么我在此时标注红圈,那么如果要获取红圈相对于原图的位置,我的红圈要向左-50px,才对,对的就是这个思路,但是,这里鼠标的移动量等于图片的移动量,这也就是@1的问题所在,对于它的api,没有/scale的话,鼠标移动的距离根本不是图片移动的距离,小于图片移动的距离(多少取决于scale),也就是鼠标向右移动了50px,实际图片移动了60px,那么你在用红圈的点-50px,得到的并不是实际相对于图片的位置

以上种种也就是/scale的原因,至于为什么/scale,而不是/scale2,而不是/scale3?这要看它的translation的api是怎么定义的,我是在他官方的例子中找到的答案,所以在这里/scale,在下文在乘以scale,确保公式的定义的鼠标移动量。

先保证图片和鼠标同步移动,然后再使用公式