mindboggle
mindboggle copied to clipboard
Decimate does not work well with three.js
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):

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)

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)
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):

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 -- 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 Yes, that's my belief.
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?