nipy / mindboggle

Automated anatomical brain label/shape analysis software (+ website)
http://mindboggle.info
Other
145 stars 54 forks source link

Decimate does not work well with three.js #60

Open bcipolli opened 8 years ago

bcipolli commented 8 years ago

If I decimate the individual ROI surfaces, parse using VTKLoader.js and try to display via three.js, I get about 33% of the surfaces having invalid vertex indices that cause a javascript error (see below):

(note: If I decimate the label vtk before exploding into ROIs, I don't have the problem, but the result doesn't look great): image

This is either an issue with decimate or the VTKLoader code. @binarybottle could you help debug? A script to reproduce this issue is attached at the bottom (requires flask)

image

import glob
import json
import os
import threading
import webbrowser

import flask

from mindboggle.mio.vtks import freesurfer_surface_to_vtk, freesurfer_annot_to_vtk, explode_scalars
from mindboggle.guts.mesh import decimate_file

subj_path = os.environ['SUBJECTS_DIR']
fsavg_path = os.path.join(subj_path, 'fsaverage')

surf_file = os.path.join(fsavg_path, 'surf', 'lh.pial')
label_file = os.path.join(fsavg_path, 'label', 'lh.aparc.annot')

surf_vtk = 'lh_surf.vtk'
label_vtk = 'lh_label.vtk'

# Convert surface and labels to vtk, break into ROIs
print('Generating vtks')
if not os.path.exists(surf_vtk):
    freesurfer_surface_to_vtk(surf_file, surf_vtk)
if not os.path.exists(label_vtk):
    freesurfer_annot_to_vtk(label_file, surf_vtk, label_vtk)

# Break into ROIs
if True:
    # downsample_vtk(label_vtk, sample_rate=sample_rate)
    explode_scalars(label_vtk, output_stem='lh_roi_')
roi_dict = dict([(i, roi_vtk) for i, roi_vtk in enumerate(glob.glob('lh_roi_*.vtk'))])

    # Downsample
if True:
    print('Downsampling vtks')
    for roi_vtk in roi_dict.values():
        decimate_file(roi_vtk, reduction=0.5, output_vtk=roi_vtk,
                      save_vtk=True, smooth_steps=0)

# Create manifest file
with open('lh.json', 'wb') as fp:
    json.dump(dict(filename=roi_dict), fp)

# Create index.html
with open('bug.html', 'wb') as fp:
    fp.write("""
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - loaders - vtk loader</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link rel="stylesheet" type="text/css" href="http://cseweb.ucsd.edu/~bcipolli/roygbiv/style.css" />

        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/three.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/Projector.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/Detector.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/TrackballControls.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/VTKLoader.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/jquery.min.js"></script>
        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/js/angular.min.js"></script>

        <script src="http://cseweb.ucsd.edu/~bcipolli/roygbiv/brain.js"></script>
    </head>

    <body>
        <div ng-app="navigator" ng-controller="NavigateController" ng-strict-di>
            <div id="nav-brain">
            </div>
        </div>

        <script>
            angular.module('navigator', [])
            .controller('NavigateController', ['$scope', function($scope) {
                $scope.brain = new Brain({
                    divID: "nav-brain",
                    manifest: 'lh.json'
                });
            }]);
        </script>
    </body>
</html>
""")

# Launch web server
app = flask.Flask('foo')

@app.route('/<path:path>')
def send_all(path):
    return flask.send_from_directory('.', path)

threading.Timer(2.5, lambda: webbrowser.open('http://127.0.0.1:5121/bug.html')).start()
#
#print('Launching server.')
#app.debug = True
app.run(port=5121)
bcipolli commented 8 years ago

My current workaround: if, after decimating, I call explode_scalars to simply read/rewrite the file, it works (edit: workaround using read_vtk/write_vtk is also good): image

I'm not sure if the file format is not compatible with three.js, or if the vtk loading code is simply more robust to errors.

binarybottle commented 8 years ago

@bcipolli -- So that I'm clear, do you believe that the decimation function is saving the files in a format not compatible with three.js, and that reading/rewriting (with explode_scalars() or with read_vtk()/write_vtk()) results in a three.js-amenable format?

bcipolli commented 8 years ago

@binarybottle Yes, that's my belief.

binarybottle commented 8 years ago

Perhaps writing out the vtk file using vtk.vtkPolyDataWriter() in the decimate() function might not be the way to go. I'm not sure what to replace it with, but given that you found a workaround, would you mind submitting a pull request to include the read/rewrite so that the output of the function will behave well for future roygbiv/three.js work? Were you able to do so for files with multiple labels?