mindboggle icon indicating copy to clipboard operation
mindboggle copied to clipboard

Decimate does not work well with three.js

Open bcipolli opened this issue 10 years ago • 4 comments

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 avatar Oct 07 '15 14:10 bcipolli

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.

bcipolli avatar Oct 07 '15 14:10 bcipolli

@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?

binarybottle avatar Feb 29 '16 06:02 binarybottle

@binarybottle Yes, that's my belief.

bcipolli avatar Feb 29 '16 14:02 bcipolli

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?

binarybottle avatar Mar 02 '16 03:03 binarybottle