NF optimize 4D timeseries performance and add interactive timeseries panel - #745
Conversation
|
@claude review |
|
Claude finished @jwparks's task in 9m 56s —— View job Claude's ReviewTasks
Nice PR overall — the memory-copy reductions in Findings1. 2. Blocking 3. Lazy packaging doesn't cover Minor / non-blocking
None of these are blockers for the core approach, but #1/#2 mean the "server responsive during startup" story in the description doesn't quite match what the code does yet, and #3 is a gap worth a conscious decision either way. |
|
For user-level tests (adding my own data, working with the buttons), this seems to work well. Claude has some praise for the efficiency & approach (but then claude probably wrote much of it). Notes for integration with other new GUI elements: there is a figure.js file added to handle the creation of the timeseries window (which appears at the bottom of the webgl window). Seems fine and good. AFAIK, other GUI elements will have pop-up windows or elements incorporated into the extant fold-down menu, so I don't see any obvious collisions or incompatibilities. Looks like some re-basing may be necessary before merge; aside from that, this looks great, I am for it. |
|
Hmmm a point to maybe discuss: the save image button does not capture the timeseries window if it is visible. Should it? I can see arguments both ways: on one hand, there are many ways to make timeseries plots, and the main function of pycortex should stay making brain images. On the other, if a window is visible, some ppl might want or expect save image to capture it. If save image saves all slice views in @candytaco 's new PR (haven't looked at it yet) maybe it should capture timeseries window. too...? Thoughts? |
# Conflicts: # cortex/webgl/view.py
|
I merged the latest main, and reworked the startup order in view.py based on the review. Handlers now wait for the data on a worker thread instead of blocking the server's event loop, so the server stays responsive while it prepares. If preparation fails, waiting requests get a 503 and the error is raised. Also, VolumeRGB does get the lazy-packing win. |
# Conflicts: # cortex/webgl/resources/js/dataset.js
|
I merged upstream main into this branch and added some fixes to the timeseries panel. Fixes
|
|
CI failure is |
|
I'm merging this since the one test failure is flaky and unrelated to this PR. That's something we can fix with the other CI-related issues (e.g. #756). |
Resolves #730
While pycortex previously supported 4D data, the implementation was somewhat inefficient and slow. This PR improves the overall speed and algorithms for processing 4D timeseries data and adds a new time-series panel to the pycortex viewer. Additionally, I've included two example scripts to demonstrate these changes. Please take a look and review! #
Added files
These are example scripts to demonstrate timeseries functionalities
examples/timeseries/README.txtexamples/timeseries/encoding_model_timeseries.pyexamples/timeseries/rgb_timeseries.pyEdited files
cortex/dataset/braindata.pymin/max in to_json (_nan_to_num_bounds() on line 714)
The original calls np.nan_to_num(data).min() and then .max(). nan_to_num copies the whole array, so this makes two full copies just to read two numbers. For 4D movies, that's several GB allocated, which was a large part of the viewer's startup time. I changed it to walk the array one slice at a time and keep a running min/max. The result is identical because nan_to_num is elementwise and min/max are associative, but only one slice is in memory at a time and the data is converted once instead of twice.
hashing
The original hashes array.tobytes(), which also copies the whole array first (which is the slow part for 4D data). I changed it to hash the array's buffer directly through a memoryview.
cortex/webgl/data.pyPackaging renders every frame of a 4D dataset into a PNG mosaic before show() returns. The original implementation took several minutes to load about 500 volumes of a run before the server started. I added a
lazy=Truemode that packs only frame 0 up front and packs the rest in get_image() when the browser or user asks (e.g., clicking the "timeseries" button). The viewer now opens in seconds instead of minutes.cortex/webgl/view.pyNew /timeseries endpoint
I added a handler that slices the timecourse out of the array Python already holds and returns JSON.
Startup order of show()
Original show() (e.g., webshow()) did all packaging and surface cache work before printing the URL, so the terminal looked frozen on 4D data. I start the server and print the URL first, then run the heavy background work in _prepare(), with handlers waiting on a threading.Event.
1D timeseries traces (for design matrix / regressors)
I pull plain 1D ndarrays out of the show() dict and return them with each timeseries response, so you can pass {"run": vol, "stimulus": regressor} and overlay them.
cortex/webgl/resources/js/dataset.jsA movie pulls every frame into the browser on page load (requiring a long wait for all volumes). I changed it to load volume (frame) 0 first, stream the rest in the background, and use setPriority() to re-aim the stream at the frame the user jumps to.
cortex/webgl/resources/js/mriview.jsI added a timeseries button to the movie controls, wired picks to fetch from /timeseries, and added seekFrame() so clicking a timepoint moves the brain. dataBuffersReady now checks that frame 0 exists rather than checking array length, since the texture array is sparse.
cortex/webgl/resources/js/figure.jsI added a canvas panel for timeseries with a checkbox, a color picker, and a raw / z-scored toggle button.