Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a web browser dose viewer gui to EGSnrc #630

Draft
wants to merge 200 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
200 commits
Select commit Hold shift + click to select a range
6e9dc1b
Initial commit for web-based dose viewer
ellieb Jun 8, 2020
876cd1b
Add readers for .egsphant and .3ddose files
ellieb Jun 9, 2020
de36279
Add class to create a volume viewer
ellieb Jun 9, 2020
2e6c86a
Add usage instructions to README
ellieb Jun 9, 2020
5e93b7a
Add css file to style the dose viewer
ellieb Jun 9, 2020
5ce87c3
Add support for view plane change
ellieb Jun 10, 2020
79bae5f
Add slice number data slider and Cartesian axes
ellieb Jun 10, 2020
6ea9547
Add dose contours
ellieb Jun 11, 2020
7a30e6e
Clean up get slice and only redraw axes if needed
ellieb Jun 12, 2020
f85cf15
Update .3ddose file reader
ellieb Jun 11, 2020
7ddef7c
Add error array and store dose as sparse array
ellieb Jun 12, 2020
5b1989e
Create volume base class
ellieb Jun 12, 2020
1328853
Add legend for dose and density levels
ellieb Jun 12, 2020
97e7355
Adjust appearance of dose contours and density map
ellieb Jun 15, 2020
f8274d0
Add dose profile plotting
ellieb Jun 16, 2020
f959cc7
Add voxel coordinates and marker upon mouse click
ellieb Jun 16, 2020
ac35c91
Add increment and decrement buttons to slider
ellieb Jun 17, 2020
35fa1be
Add zooming capabilities to the dose viewer gui
ellieb Jun 18, 2020
28b6f5c
Improve the density and dose plot display
ellieb Jun 19, 2020
b963794
Improve the dose profile plot code and display
ellieb Jun 20, 2020
faad2dd
Add dose profiles to crosshairs
ellieb Jun 23, 2020
081d156
Add dose uncertainty to voxel information
ellieb Jun 23, 2020
786c1e2
Create a dose profile object
ellieb Jun 24, 2020
c1b3917
Extract zoom logic from index.html into zoom.js
ellieb Jun 24, 2020
35a9810
Add zooming capabilities for dose profiles
ellieb Jun 24, 2020
3fefb5a
Add the ability to export view to a png image
ellieb Jun 25, 2020
a3352f1
Fix issues with density and dose plots
ellieb Jun 25, 2020
6aba0c2
Add enable/disable checkbox for dose profile plots
ellieb Jun 26, 2020
5183ac8
Convert size variables into objects
ellieb Jun 29, 2020
104be81
Maintain image aspect ratio and add axes grid
ellieb Jun 30, 2020
ad0792c
Improve the presentation of dose plots
ellieb Jun 30, 2020
7f780ae
Allow selecting interval between dose contours
ellieb Jul 2, 2020
b8f962b
Improve the layout of dose profile plots
ellieb Jul 2, 2020
15f2ca0
Improve zooming in dose profile plots
ellieb Jul 3, 2020
f627d59
Use world coordinates in dose profile plot title
ellieb Jul 5, 2020
1cd2b66
Define a dose profile data variable to return data
ellieb Jul 6, 2020
42a6f10
Improve the style of dose plots
ellieb Jul 6, 2020
ab01780
Use a d3 scaling object to scale contours
ellieb Jul 6, 2020
3a93ca1
Improve style and behavior of dose plot contours
ellieb Jul 7, 2020
041751f
Improve style and behavior of dose profile plots
ellieb Jul 9, 2020
7eab16c
Export images as png and dose profile data as csv
ellieb Jul 14, 2020
fe88b3f
Enable file drag and drop to load data
ellieb Jul 15, 2020
101db8e
Add material to voxel data
ellieb Jul 17, 2020
8d49d35
Use an html canvas for the density and dose plot
ellieb Jul 20, 2020
7166c23
Add density level and window data sliders
ellieb Jul 23, 2020
7f39400
Improve voxel information box and dose positioning
ellieb Jul 23, 2020
5592e64
Display the 3 plots for x, y and z axes at once
ellieb Jul 28, 2020
a9286a0
Improve the style of dose and profile plots
ellieb Jul 29, 2020
de3e3d9
Add panel class and allow dragging of crosshairs
ellieb Aug 3, 2020
1eac602
Add zooming to panel, after object initialization
ellieb Aug 4, 2020
6734b29
Show correct voxel coordinates on drag
ellieb Aug 4, 2020
893b122
Add axis to prevSlice and prevSliceImg
ellieb Aug 6, 2020
2fa1d45
Move through slices by clicking on the panel
ellieb Aug 6, 2020
33c92cd
Improve panel style and fix panel zoom
ellieb Aug 7, 2020
f11c9b3
Sync cursor on click and marker position on drop
ellieb Aug 10, 2020
3f2369c
Improve layout of density and profile plots
ellieb Aug 10, 2020
8b1df39
Fix dose profile zoom and drag marker behavior
ellieb Aug 11, 2020
e319c4a
Add a slider class, and a slider for each panel
ellieb Aug 13, 2020
65e251e
Define a volume viewer object
ellieb Aug 18, 2020
2dda07c
Adapt dose viewer elements to volume viewer object
ellieb Aug 18, 2020
7af797f
Fix slice number sliders and voxel information
ellieb Aug 18, 2020
ee38097
Support dose profiles within volume viewer object
ellieb Aug 19, 2020
4e5a9fc
Fix zoom, and marker drag and drop behavior
ellieb Aug 19, 2020
f7e3ec6
Improve layout of voxel info and dose profiles
ellieb Aug 19, 2020
5fd51b3
Add the ability to load multiple volume viewers
ellieb Aug 20, 2020
39c88ee
Load voxel and dose data at correct slice and zoom
ellieb Aug 21, 2020
37406ae
Add dose file comparison dropdown selection
ellieb Aug 21, 2020
70cb2fa
Enable comparisons between dose distributions
ellieb Aug 24, 2020
c77c8dc
Clean up code and layout of the dose viewer
ellieb Aug 25, 2020
c26c261
Load on center slice and open new viewer if needed
ellieb Aug 28, 2020
ca5d5f5
Add the option to load test file data
ellieb Aug 28, 2020
e417e1b
Export with dom-to-image instead of html2canvas
ellieb Aug 27, 2020
7441fc5
Rename create-volume.js script file to volume.js
ellieb Aug 28, 2020
d247a28
Add University of Victoria XCITE lab logo
ellieb Aug 28, 2020
a2be6fd
Comment and document the dose viewer code
ellieb Aug 29, 2020
4903617
Clean up and polish dose viewer gui elements
ellieb Aug 31, 2020
659a052
Add EGSnrc headers and update web page title
ellieb Aug 31, 2020
2e32d7c
Format dose viewer code with Standard and Prettier
ftessier Sep 8, 2020
66e7b26
Format the javascript files with StandardJS
ftessier Sep 15, 2020
cbce49d
Manually adjust the format of javascript files
ftessier Sep 15, 2020
a179216
Adjust html and css style with Prettier formatter
ftessier Sep 15, 2020
574ba8b
Use ES6 import and export statements
ellieb Oct 8, 2020
6e82676
View single DICOM file
ellieb Nov 10, 2020
b83141b
Remove old window and level sliders on volume change
ellieb Nov 16, 2020
1524f88
Add min density value to volume
ellieb Nov 20, 2020
23437c1
Change window level density sliders to min max density sliders
ellieb Nov 23, 2020
b61edc1
Remove unnecessary console log statement
ellieb Nov 30, 2020
6bbf537
Read in multiple DICOM files
ellieb Nov 30, 2020
0845c5b
Change density formatting depending on if DICOM or egsphant file
ellieb Dec 1, 2020
6baa724
Flip the y axis on the xy panel
ellieb Dec 1, 2020
78e7eaf
Lock min and max sliders so they cannot pass eachother
ellieb Dec 1, 2020
465a487
Support DICOM dose files
ellieb Dec 11, 2020
f9ffd4a
Handle multi-frame dose DICOM files
ellieb Dec 13, 2020
d1c66c2
Correctly display dose and density with different dimensions and move…
ellieb Dec 13, 2020
8d4e96c
Add min density to test file data
ellieb Dec 18, 2020
6a1d36c
Fix bug in error of uneven offset length in pixel data
ellieb Dec 18, 2020
763f024
Remove extra console logging on DICOM file upload
ellieb Dec 18, 2020
521159d
Do not plot density error if it does not exist
ellieb Dec 18, 2020
7bce380
Change updateVoxelCoords to use positional coordinates rather than vo…
ellieb Dec 18, 2020
495b5ae
Fix bug in reading egsphant files
ellieb Jan 4, 2021
8731259
Add slice and density image caching
ellieb Jan 7, 2021
d951ced
Automatically cache density slices
ellieb Jan 9, 2021
dd82074
Use array to create density images to solve resolution issue
ellieb Jan 14, 2021
bca9db5
Add web worker to cache density slices in background
ellieb Jan 19, 2021
02e9bef
Allow slice caching in different browsers
ellieb Jan 21, 2021
70df3a3
Fix min and max sliders to work with cached slices
ellieb Jan 24, 2021
628d9a6
Fix slice number navigation bug
ellieb Jan 25, 2021
9b09d33
Update density legend upon min ma density change
ellieb Jan 25, 2021
e3490e8
Load both density and dose files upon upload
ellieb Jan 26, 2021
64ddcad
Allow for removal of dose and density files from volume
ellieb Jan 26, 2021
0b23ffd
Replace invertTransform function with d3 invert function
ellieb Jan 26, 2021
7b22f1b
Add baseSlices object to minimize slice calculations and remove prevS…
ellieb Jan 29, 2021
77bf497
Clean up cache worker
ellieb Jan 29, 2021
ecc3c9d
Move dose and density options inside the volume viewer
ellieb Feb 2, 2021
9fc0ca8
Redraw slice after changing dose and density sliders
ellieb Feb 2, 2021
1dbb2ad
Change density legend ticks instead of colour bar
ellieb Feb 2, 2021
2bc09d9
Adjust dose scalings if corresponding density file has different voxe…
ellieb Feb 2, 2021
996d254
Always display dose file if one or more is uploaded
ellieb Feb 3, 2021
fca9f75
Add encoding to html file
ellieb Feb 3, 2021
66ef83d
Move from module code to script code
ellieb Feb 3, 2021
44c323d
Don't make new volume viewer when loading test files
ellieb Feb 3, 2021
9a9c91d
Add global functions to file upload
ellieb Feb 3, 2021
b208d41
Fix bug where adding dose percentage does not show up
ellieb Feb 3, 2021
53139df
Filter out unrecognized file extensions instead of rejecting all files
ellieb Feb 4, 2021
4030841
Pass in svg to draw and clear dose and density
ellieb Feb 5, 2021
95a0541
Fix zoom for multiple volume viewers
ellieb Feb 5, 2021
9442bef
Add global imports and ignore unused vars to be inline with js standard
ellieb Feb 5, 2021
c4213db
Fix show voxel and plot dose profile checkboxes to work with multiple…
ellieb Feb 7, 2021
237e0db
Let min and max density sliders to work with multiple volume viewers
ellieb Feb 8, 2021
47b2edc
Let max dose slider work with multiple volume viewers
ellieb Feb 11, 2021
cbce8cc
Let max dose in volume viewer work with dose profile plots
ellieb Feb 11, 2021
81a227e
Allow export buttons to work with multiple volume viewers
ellieb Feb 11, 2021
2e20917
Update DoseComparisonVolume and add the minDose attribute to the volu…
ellieb Feb 17, 2021
0ba3f79
Only load comparable doses in dose comparison dropdown
ellieb Feb 18, 2021
78c4b63
Adjust dose profile y axis scale for dose and dose comparisons
ellieb Feb 18, 2021
00e33dc
Fix bug in coordinates shifting on dose volume change
ellieb Feb 22, 2021
c39f2d8
Calculate dose comparisons between RT Dose and 3ddose files
ellieb Feb 24, 2021
6c4b8d6
Fix bug in dose error display
ellieb Feb 24, 2021
273d14a
Fix dose comparison selector to show all avaliable doses to compare
ellieb Feb 25, 2021
0a5c7c1
Fix density legend colourbar bug
ellieb Feb 26, 2021
76dceaf
Adjust opacity of dose contours
ellieb Mar 1, 2021
9fefa08
Implement trilinear interpolation method
ellieb Mar 5, 2021
b06eac2
Move code for clearing dose and density plots from volume to panel
ellieb Mar 8, 2021
8784565
Move initialize canvas logic to panel
ellieb Mar 8, 2021
2d9418a
Move density legend code into volume viewer
ellieb Mar 8, 2021
fe24889
Read in RT Structure Set DICOM files
ellieb Mar 12, 2021
7f28d7e
Add StructureSetVolume object
ellieb Mar 15, 2021
961906d
Get basic ROI contours working
ellieb Mar 18, 2021
1532412
Add ROI legend to volume viewer
ellieb Mar 18, 2021
9e27276
Add zoom for ROI contours
ellieb Mar 18, 2021
d2c1d13
Add function to build colour legends
ellieb Mar 19, 2021
00f4a42
Index slice sliders starting at 1
ellieb Mar 19, 2021
6869626
Adjust increment of ROI array to fix gap
ellieb Mar 22, 2021
ea51b85
Update ROI contours checkbox text
ellieb Mar 22, 2021
93f648a
Add function to get volume data at world position
ellieb Mar 23, 2021
e8f3fe3
Add DVH calculation function to structure set volume object
ellieb Mar 23, 2021
59b7853
Fix bug where mutiple polygons in one slice are not drawn
ellieb Mar 24, 2021
4c969de
Increase width of ROI legend
ellieb Mar 24, 2021
8a347a9
Do not include unnecessary information in structure set volume
ellieb Mar 25, 2021
f7d7dfc
Use scanline algorithm to generate ROI array to speed up generation
ellieb Mar 26, 2021
68141b1
Bug fix for polygon scanline algo
ellieb Mar 26, 2021
addab7d
Fix ROI display bug by indexing by given ROI number
ellieb Mar 26, 2021
50bf594
Don't delete ROIs when adding dose contours
ellieb Mar 29, 2021
a2c0e70
Fix bug with dose profile not showing for dose comparisons
ellieb Mar 30, 2021
cadcd08
Add DoseVolumeHistogram class
ellieb Apr 5, 2021
cce890f
Add functionality for DVHs
ellieb Apr 5, 2021
8733e0c
Move cumulative histogram calculations to the setting data method in …
ellieb Apr 7, 2021
63c7a1f
Add export DVH to CSV button
ellieb Apr 7, 2021
c7f6e15
Return absolute dose from get data function
ellieb Apr 12, 2021
82352f3
Convert DVH from relative to absolute dose
ellieb Apr 12, 2021
d1a6f65
Don't delete ROI contours when adjusting dose contours
ellieb Apr 12, 2021
d6993be
Use generic enable and disable checkbox functions
ellieb Apr 13, 2021
09eb260
Add normalize dose checkbox and input boxes
ellieb Apr 13, 2021
b13c4e1
Add dose normalization support for DVHs
ellieb Apr 16, 2021
bc9e835
Disable and uncheck DVH checkbox when ROI contours is unselected
ellieb Apr 16, 2021
8b8c75f
Only show DVH checkbox if dose volume loaded and show ROI contours ch…
ellieb Apr 16, 2021
51b4581
Add zoom for DVH plot
ellieb Apr 18, 2021
c2a787f
Include StudyInstanceUID in volume information
ellieb Apr 19, 2021
6efa9ce
Match stucture set to displayed density/dose
ellieb Apr 19, 2021
699a429
Properly convert from Gy to cGy for dose normalization
ellieb Apr 20, 2021
4b47245
Add dose units to dose volumes
ellieb Apr 22, 2021
f437688
Update checkbox behaviours to better work together
ellieb Apr 22, 2021
7e68a6d
Simplify zoom for dose profiles
ellieb Apr 22, 2021
c13c8e9
Minor css style changes
ellieb Apr 23, 2021
3e8c99c
Add dose comparison normalization slider
ellieb Apr 26, 2021
5bdfb5c
Remove max dose and dose comparison normalization sliders when not us…
ellieb Apr 26, 2021
ace900c
Add function to simply update voxel information from volume viewer
ellieb Apr 26, 2021
0ff9fd1
Organize volumes to simplify file structure
ellieb Apr 26, 2021
efb0799
Organize sliders to simplify file structure
ellieb Apr 26, 2021
300a74c
Fix spelling and casing errors
ellieb Apr 27, 2021
3e2b1e3
Update test files
ellieb Apr 27, 2021
226ef37
Add slider file for slice slider
ellieb Apr 27, 2021
3e33ee0
Fix RT DICOM scaling issue
ellieb Apr 27, 2021
26faa88
Adjust font size
ellieb Apr 28, 2021
d12f2ce
Only keep scroll for ROI legend
ellieb Apr 28, 2021
686ed1e
Update colours used for plot grids and labels
ellieb Apr 28, 2021
e1d64de
Line up lengend ends
ellieb Apr 28, 2021
fad3422
Export dose profiles in single CSV file
ellieb May 18, 2021
0180e96
Update min and max density slider labels
ellieb May 18, 2021
abb1582
Change cursor to pointer over panels
ellieb Jul 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions HEN_HOUSE/gui/dose-viewer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ignore local data
data/
assets/

# ignore hidden system files
.DS_Store
3 changes: 3 additions & 0 deletions HEN_HOUSE/gui/dose-viewer/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Ignore Javascript files (they are formatted with JS Standard)
*.js
*.mjs
1 change: 1 addition & 0 deletions HEN_HOUSE/gui/dose-viewer/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

34 changes: 34 additions & 0 deletions HEN_HOUSE/gui/dose-viewer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Dose Viewer

Dose viewer is a (WIP) web-based visualization tool for EGSnrc phantom and dose
files. To use, either upload .3ddose and .egsphant files or use the test files
available. If two or more dose files are uploaded, they can be compared in the
viewer. Dose contours can be added with the input box in the dose legend.
Contours can be toggled on and off by clicking the corresponding colour block in
the legend. Voxel information can be viewed by selecting the checkbox to show
voxel data and selecting a point in any of the three plots. Dose profiles can be
viewed by selecting the checkbox to plot dose profiles and choosing a point on
the plot to investigate.

## Usage

To run this webpage locally:

1. Clone the repo
2. Using the command line, `cd` into the dose-viewer folder
3. If you have Python 3, start a local server using the command

python -m http.server

If you have Python 2, use the command

python -m SimpleHTTPServer

4. If the command is successful, it should give you a link (e.g.
http://0.0.0.0:8000/) which you can paste into your browser to view the web page

## Acknowledgements

This project is a collaboration between Elise Badun and Magdalena
Bazalova-Carter of the University of Victoria, and Frederic Tessier, Reid
Townson, and Ernesto Mainegra-Hing of the NRC.
144 changes: 144 additions & 0 deletions HEN_HOUSE/gui/dose-viewer/cache-worker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* global addEventListener */
/* global close */
/* global d3 */
/* global importScripts */
/* global postMessage */
/* global removeEventListener */

if (typeof importScripts === 'function') {
importScripts('https://d3js.org/d3-color.v2.min.js')
importScripts('https://d3js.org/d3-scale.v3.min.js')
importScripts('https://d3js.org/d3-interpolate.v2.min.js')
importScripts('https://d3js.org/d3-scale-chromatic.v2.min.js')
addEventListener('message', handleMessage)
}

function handleMessage (e) {
// Process position to get centre voxel position rather than boundaries
var position = e.data.voxArr
position = position.map((val, i) => {
return val + (e.data.voxArr[i + 1] - val) / 2
})
position.pop()

// Get the function to get colour
const colourFn = getColourFunction(e.data.minVal, e.data.maxVal)

// TODO: Modify getSlice to take sliceNum instead of pos!
// Get and cache image for each slice in axis
const promiseImgCache = position.map((slicePos) => {
const slice = getSlice(e.data.data, e.data.dimensions, e.data.axis, slicePos, 'density')
return getDataArray(slice, colourFn)
})

Promise.all(promiseImgCache).then(imgCache => {
postMessage(imgCache)
removeEventListener('message', handleMessage)
close()
})
}

/**
* Create the function to return the pixel colour of each pixel value
*
* @param {number} minVal The minimum density value
* @param {number} maxVal The maximum density value
* @returns {getColourFunction~colourFn}
*/
function getColourFunction (minVal, maxVal) {
return d3.scaleSqrt().domain([minVal, maxVal]).range([0, 255])
}

/**
* Returns the data image of the slice
*
* @param {Object} slice The slice of the density data.
* @returns {number[]}
*/
function getDataArray (slice, colourFn) {
// Create the image data
var imageData = new Uint8ClampedArray(slice.xVoxels * slice.yVoxels * 4)
var j = 0
for (let i = 0; i < slice.sliceData.length; i++) {
const val = colourFn(slice.sliceData[i])

if (val !== null) {
// Modify pixel data
imageData[j++] = val // R value
imageData[j++] = val // G value
imageData[j++] = val // B value
imageData[j++] = 255 // A value
}
}

return imageData
}

/**
* Get a slice of data through an axis.
*
* @param {string} axis The axis of the slice (xy, yz, or xz).
* @param {number} sliceNum The number of the slice.
* @param {string} dataName The type of data, either "density" or "dose".
* @returns {Object}
*/
function getSlice (data, dimensions, axis, slicePos, dataName) {
// TODO: Cache previous slices
// TODO: Only redefine slice attributes on axis change
// For slice structure
// /~https://github.com/nrc-cnrc/EGSnrc/blob/master/HEN_HOUSE/omega/progs/dosxyz_show/dosxyz_show.c#L1502-L1546

// Get the axes and slice dimensions
const [dim1, dim2, dim3] =
axis === 'xy'
? ['x', 'y', 'z']
: axis === 'yz'
? ['y', 'z', 'x']
: ['x', 'z', 'y']

const xVoxels = data.voxelNumber[dim1]
const yVoxels = data.voxelNumber[dim2]

// Find zScale to get sliceNum from slicePos
const z = data.voxelArr[dim3]
const totalSlices = data.voxelNumber[dim3] - 1
const zDomain = [z[0], z[z.length - 1]]
const zRange = [0, totalSlices]
const zScale = (val) => (
zRange[0] + (zRange[1] - zRange[0]) * ((val - zDomain[0]) / (zDomain[1] - zDomain[0]))
)
const sliceNum = Math.round(zScale(slicePos))

// Get the slice data for the given axis and index
// For address calculations:
// /~https://github.com/nrc-cnrc/EGSnrc/blob/master/HEN_HOUSE/omega/progs/dosxyz_show/dosxyz_show.c#L1999-L2034
const sliceData = new Array(xVoxels * yVoxels)

for (let i = 0; i < xVoxels; i++) {
for (let j = 0; j < yVoxels; j++) {
let address
if (axis === 'xy') {
address = i + xVoxels * (j + sliceNum * yVoxels)
} else if (axis === 'yz') {
address =
sliceNum + data.voxelNumber.x * (i + j * xVoxels)
} else if (axis === 'xz') {
address =
i + xVoxels * (sliceNum + j * data.voxelNumber.y)
}
const newAddress = i + xVoxels * j
sliceData[newAddress] = data[dataName][address]
}
}

const slice = {
xVoxels: xVoxels,
yVoxels: yVoxels,
slicePos: slicePos,
sliceData: sliceData,
sliceNum: sliceNum,
axis: axis
}

return slice
}
Loading