使用 XTK 或 AMI.js 在 T1.mgz 上显示 Freesurfer while/pial 个对象

Using XTK or AMI.js to display Freesurfer while/pial objects on T1.mgz

我想重新创建一个网络版的 FreeSurfer pial/white 表面覆盖在 T1.mgz 上,类似于 https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/PialEdits_freeview. Using XTK I can get something that hints at that using advice from Othographic Projection in XTK 上的第一个 freeview 图像。我用来创建图像的代码(以及多次尝试的 kruft)在图像下方。

XTK 是否可行,或者我应该切换到 AMI.js(其路线图上有 freesurfer 表面和 MGZ 文件格式,但尚未实施)?

在任何一种情况下,如果能指出如何实现这一点,我们将不胜感激。

谢谢。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>FS XTK test</title>
  </head>
  <body>
    <script type="text/javascript" src="/Xdevel/lib/google-closure-library/closure/goog/base.js"></script>
    <script type="text/javascript" src="/Xdevel/xtk-deps.js"></script>
    <script type="text/javascript" src="/Xdevel/xtk_xdat.gui.js"></script>
    <script type="text/javascript">
      var view2D_X = null;
      var view2D_Y = null;
      var view2D_Z = null;
      var view3D = null;
      var volume3D = null;
      var meshes = new Array(6);
      var meshFiles = new Array(6);

      var t1File = 'T1.mgz';
      meshFiles[0]='lh.orig';
      meshFiles[1]='rh.orig';
      meshFiles[2]='lh.pial';
      meshFiles[3]='rh.pial';
      meshFiles[4]='lh.white.pial';
      meshFiles[5]='rh.white.pial';

      var colors = [ //Matlab jet(28)
              [         0,         1,         0],
              [         0,         1,         0],
              [         1,         0,         0],
              [         1,         0,         0],
              [         0,         0,         1],
              [         0,         0,         1]
      ];


      function setView(pos)
      {
        switch(pos)
        {
          case 1:
            camPos=[ 0,  0, -1, 0,
                    -1,  0, -0, 0,
                     0,  1,  0, 0,
                     1,  0, -1, 1];
            break;
          case 2:
            camPos=[-1,  0,  0, 0,
                     0,  0,  1, 0,
                     0,  1, -0, 0,
                     0, -1, -1, 1];
            break;
          default: //Case 3
            camPos=[-1,  0, -0, 0,
                    -0,  1, -0, 0,
                     0,  0,  1, 0,
                     0, -0, -1, 1];
          break;
        }
        camPos[14] = 200*camPos[14]; //zoomout
        view3D.camera.view=new Float32Array(camPos);
      }

      // include all used X-classes here
      // this is only required when using the xtk-deps.js file
      goog.require('X.renderer2D');
      goog.require('X.renderer3D');
      goog.require('X.mesh');

      function addLoadEvent(func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
          window.onload = func;
        } else {
          window.onload = function() {
            if (oldonload) {
              oldonload();
            }
            func();
          }
        }
      }
    </script>

    <div id="view3D_div" style="background-color: #000; width: 399px; height: 399px;"></div>
    <script type="text/javascript">
      function loadMeshes()
      {
        for (var a = 0; a < 6; a++)
        {
          try
          {
            meshes[a] = new X.mesh();
            meshes[a].file=meshFiles[a];
            meshes[a].color = colors[a];
            meshes[a].visible=true;
            view3D.add(meshes[a]);
          }
          catch(err)
          {
            console.log('failed to load: '+meshFiles[a]);
            console.log(err.message);
          }
        }
      }

      var _meshConfig = {
        'width' : 399,
        'height' : 399,
        'unknown' : 180.5,
        'diff' : 0.3
      };

      function setMainSlice()
      {
//        console.log('height: ' + height + ' width: ' + width);
        console.log('X: ' + volume3D.indexX + 'Y: ' + volume3D.indexY + 'Z: ' + volume3D.indexZ);
        _meshConfig.unknown=volume3D.indexZ+92.5;
        console.log('width: '+_meshConfig.width + ' height: ' + _meshConfig.height + ' unknown: ' + _meshConfig.unknown);
        view3D.camera._perspective=X.matrix.makeOrtho(X.matrix.identity(), -(_meshConfig.width/2), (_meshConfig.width/2), -(_meshConfig.height/2), (_meshConfig.height/2), _meshConfig.unknown+_meshConfig.diff, _meshConfig.unknown-_meshConfig.diff);
//        view3D.camera._perspective=goog.vec.Mat4.createFromValues(1,0,0,0,0,1,0,0,0,0,1,0,volume3D.indexX,volume3D.indexY,volume3D.indexZ,1);
//        view3D.camera._perspective=goog.vec.Mat4.createFromValues(0.005,0,0,0, 0,0.005,0,0, 0,0,3,0, 0,0,256+(volume3D.indexZ*2),1);
      }

      addLoadEvent(function () {
        view3D = new X.renderer3D();
        view3D.container = 'view3D_div';
        view3D.init();
        volume3D = new X.volume();
        volume3D.file = t1File;
//        volume3D.labelmap.file='all.white.mgz';

        view3D.add(volume3D);

        loadMeshes();
        setView(3);
//              view3D.camera.position=[-0, 0, 90];
//              view3D.camera.view[14] = -200;
        view3D.render();

        view3D.onShowtime = function () {
          view2D_X.onScroll = setMainSlice;
          view2D_X.add(volume3D);
          view2D_X.render();
          view2D_Y.onScroll = setMainSlice;
          view2D_Y.add(volume3D);
          view2D_Y.render();
          view2D_Z.onScroll = setMainSlice;
          view2D_Z.add(volume3D);
          view2D_Z.render();
          setView(3);
        };


        var gui = new dat.GUI();
        var anat_folder = gui.addFolder('T1');
        anat_folder.add(volume3D,'visible');
        anat_folder.add(volume3D,'opacity',0,1);
        anat_folder.add(volume3D,'indexX');
        anat_folder.add(volume3D,'indexY');
        anat_folder.add(volume3D,'indexZ',0,256);
        anat_folder.open();
        var lh_orig_folder = gui.addFolder('Freesurfer lh.orig');
        lh_orig_folder.add(meshes[0],'visible');
        lh_orig_folder.add(meshes[0],'opacity',0,1);
        lh_orig_folder.addColor(meshes[0],'color');
//        lh_orig_folder.open();
        var rh_orig_folder = gui.addFolder('Freesurfer rh.orig');
        rh_orig_folder.add(meshes[1],'visible');
        rh_orig_folder.add(meshes[1],'opacity',0,1);
        rh_orig_folder.addColor(meshes[1],'color');
//        rh_orig_folder.open();
        var lh_pial_folder = gui.addFolder('Freesurfer lh.pial');
        lh_pial_folder.add(meshes[2],'visible');
        lh_pial_folder.add(meshes[2],'opacity',0,1);
        lh_pial_folder.addColor(meshes[2],'color');
//        lh_pial_folder.open();
        var rh_pial_folder = gui.addFolder('Freesurfer rh.pial');
        rh_pial_folder.add(meshes[3],'visible');
        rh_pial_folder.add(meshes[3],'opacity',0,1);
        rh_pial_folder.addColor(meshes[3],'color');
//        rh_pial_folder.open();
        var lh_white_folder = gui.addFolder('Freesurfer lh.white');
        lh_white_folder.add(meshes[4],'visible');
        lh_white_folder.add(meshes[4],'opacity',0,1);
        lh_white_folder.addColor(meshes[4],'color');
//        lh_white_folder.open();
        var rh_white_folder = gui.addFolder('Freesurfer rh.white');
        rh_white_folder.add(meshes[5],'visible');
        rh_white_folder.add(meshes[5],'opacity',0,1);
        rh_white_folder.addColor(meshes[5],'color');
//        rh_white_folder.open();
        var mesh_folder = gui.addFolder('Mesh');
        mesh_folder.add(_meshConfig,'height');
        mesh_folder.add(_meshConfig,'width');
        mesh_folder.add(_meshConfig,'unknown');
        mesh_folder.open();

        for (c in gui.__controllers)
        {
          gui.__controllers[c].onFinishChange(update);
        }
      });


    </script>


    <table style="border-collapse: collapse">
      <tr>
        <td style="background-color: red;">
          <div id="view2D_X_div" style="background-color: #000; width: 131px; height: 131px;"></div>
          <script type="text/javascript">
            addLoadEvent(function () {
              view2D_X = new X.renderer2D();
              view2D_X.container = 'view2D_X_div';
              view2D_X.orientation = 'X';
              view2D_X.init();
            });
          </script>
        </td>
        <td style="background-color: green;">
          <div id="view2D_Y_div" style="background-color: #000; width: 131px; height: 131px;"></div>
          <script type="text/javascript">
            addLoadEvent(function () {
              view2D_Y = new X.renderer2D();
              view2D_Y.container = 'view2D_Y_div';
              view2D_Y.orientation = 'Y';
              view2D_Y.init();
            });
          </script>
        </td>
        <td style="background-color: blue;">
          <div id="view2D_Z_div" style="background-color: #000; width: 131px; height: 131px;"></div>
          <script type="text/javascript">
            addLoadEvent(function () {
              view2D_Z = new X.renderer2D();
              view2D_Z.container = 'view2D_Z_div';
              view2D_Z.orientation = 'Z';
              view2D_Z.init();
            });
          </script>
        </td>
      </tr>
      <tr>
        <td style="background-color: red;">
<!--                <button onClick="setView([-90,0,0]);">Set View</button>-->
          <button onClick="setView(1);">Set View</button>
        </td>
        <td style="background-color: green;">
<!--                <button onClick="setView([0,90,0]);">Set View</button>-->
          <button onClick="setView(2);">Set View</button>
        </td>
        <td style="background-color: blue;">
<!--                <button onClick="setView([0,0,90]);">Set View</button>-->
          <button onClick="setView(3);">Set View</button>
        </td>
      </tr>
    </table>

    Green is orig<br>
    Red is pial<br>
    Blue is white<br>


  </body>
</html>

您现在可以在 AMI 中执行此操作(感谢您的 PR:https://fnndsc.github.io/ami/#viewers_quadview

  1. 显示网格与平面的交集

  2. Post 处理交叉点以显示等高线。

有不同的技术来显示 mesh/plane 交叉点:

  1. 使用模板缓冲区(https://github.com/daign/clipping-with-caps)
  2. 播放网格不透明度(https://github.com/FNNDSC/ami/tree/dev/examples/viewers_quadview)

所有这些技术在计算上都是昂贵的,因为它需要 3 个渲染通道来显示 1 个网格的轮廓,可能有更好的方法,但不确定什么是最好的选择。

HTH