Last active
December 21, 2019 00:44
-
-
Save jkrumbiegel/bd98cf311f94b589272ad8c0e0ca60b3 to your computer and use it in GitHub Desktop.
Makie brain viewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Makie | |
using MakieLayout | |
using NIfTI | |
nipath = "T1.nii.gz" | |
nidata = niread(nipath) | |
data = Float32.(nidata) ./ maximum(nidata) | |
function axhline!(la::LAxis, y::Node; kwargs...) | |
points = lift(la.limits, y) do lims, y | |
x1 = lims.origin[1] | |
x2 = lims.origin[1] + lims.widths[1] | |
Point2f0.([(x1, y), (x2, y)]) | |
end | |
lines!(la, points; xautolimit=false, kwargs...) | |
end | |
function axvline!(la::LAxis, x::Node; kwargs...) | |
points = lift(la.limits, x) do lims, x | |
y1 = lims.origin[2] | |
y2 = lims.origin[2] + lims.widths[2] | |
Point2f0.([(x, y1), (x, y2)]) | |
end | |
lines!(la, points; yautolimit=false, kwargs...) | |
end | |
begin | |
scene, gl = layoutscene(30) | |
ax_xy = LAxis(scene, title = "XY Plane", aspect = DataAspect()) | |
ax_xz = LAxis(scene, title = "XZ Plane", aspect = DataAspect()) | |
ax_yz = LAxis(scene, title = "YZ Plane", aspect = DataAspect()) | |
tightlimits!.([ax_xy, ax_xz, ax_yz]) | |
ix = Node(1) | |
iy = Node(1) | |
iz = Node(1) | |
inodes = (ix, iy, iz) | |
crange = extrema(data) | |
hm_xy = heatmap!(ax_xy, @lift(data[:, :, $iz]), colorrange = crange, colormap = :grays) | |
hm_xz = heatmap!(ax_xz, @lift(data[:, $iy, :]), colorrange = crange, colormap = :grays) | |
hm_yz = heatmap!(ax_yz, @lift(data[$ix, :, :]), colorrange = crange, colormap = :grays) | |
cbar = LColorbar(scene, hm_xy, width = 50) | |
colors = (:red, :green, :blue) | |
# make the vertical and horizontal lines in the three slice views | |
for (ax, (x, y)) in zip((ax_xy, ax_xz, ax_yz), [(1, 2), (1, 3), (2, 3)]) | |
axhline!(ax, inodes[y], color = colors[y], linewidth = 4) | |
axvline!(ax, inodes[x], color = colors[x], linewidth = 4) | |
end | |
# add mouse event handling to the heatmaps | |
xystate = MakieLayout.addmousestate!(scene, hm_xy) | |
xzstate = MakieLayout.addmousestate!(scene, hm_xz) | |
yzstate = MakieLayout.addmousestate!(scene, hm_yz) | |
# coordinate transformation between two rects | |
function transferrects(pos, rectfrom, rectto) | |
fracpos = (pos .- rectfrom.origin) ./ rectfrom.widths | |
fracpos .* rectto.widths .+ rectto.origin | |
end | |
# connect dragging to the slice index nodes | |
MakieLayout.onmousedrag(xystate) do state | |
datapos = transferrects(state.pos, ax_xy.scene.px_area[], ax_xy.limits[]) | |
x = clamp(round(Int, datapos[1]), 1, size(data, 1)) | |
y = clamp(round(Int, datapos[2]), 1, size(data, 2)) | |
ix[] = x | |
iy[] = y | |
end | |
MakieLayout.onmousedrag(xzstate) do state | |
datapos = transferrects(state.pos, ax_xz.scene.px_area[], ax_xz.limits[]) | |
x = clamp(round(Int, datapos[1]), 1, size(data, 1)) | |
z = clamp(round(Int, datapos[2]), 1, size(data, 3)) | |
ix[] = x | |
iz[] = z | |
end | |
MakieLayout.onmousedrag(yzstate) do state | |
datapos = transferrects(state.pos, ax_yz.scene.px_area[], ax_yz.limits[]) | |
y = clamp(round(Int, datapos[1]), 1, size(data, 2)) | |
z = clamp(round(Int, datapos[2]), 1, size(data, 3)) | |
iy[] = y | |
iz[] = z | |
end | |
# this is just a rect for the 3d scene to align to because a scene is not yet | |
# layoutable by itself | |
placeholder = LRect(scene, visible = false) | |
isovalslider = LSlider(scene, range = 0:0.01:1, startvalue = 0.75) | |
isorangeslider = LSlider(scene, range = 0:0.01:1, startvalue = 0.05) | |
# layout stuff | |
gl[1, 1] = vbox!( | |
hbox!( | |
grid!( | |
[2, 1] => ax_xy, | |
[1, 1] => ax_xz, | |
[1, 2] => ax_yz, | |
[2, 2] => placeholder, | |
), | |
cbar, | |
), | |
isovalslider, | |
isorangeslider, | |
) | |
# make a child scene whose pixel area is where the placeholder rect in the layout is (hack) | |
scene3d = Scene(scene, lift(x -> IRect2D(x), placeholder.layoutnodes.computedbbox), | |
camera = cam3d!, show_axis=true) | |
scene3d.backgroundcolor = RGBf0(0.95, 0.95, 0.95) | |
scene3d.clear = true | |
# without this (useless) scatter things don't work, I don't know why | |
scatter!(scene3d, rand(100, 3), raw = false, markersize = 0.01) | |
vol = volume!(scene3d, data, algorithm = :iso, show_axis = true, | |
colormap = :heat, isovalue = isovalslider.value, | |
# isorange = isorangeslider.value, | |
)[end] | |
volbb = boundingbox(vol) | |
# make the rectangle lines in 3d | |
lines!(scene3d, lift(ix) do ix | |
rect = Rect(volbb.origin[2:3]..., volbb.widths[2:3]...) | |
points = [ | |
Point3(ix, left(rect), bottom(rect)), | |
Point3(ix, right(rect), bottom(rect)), | |
Point3(ix, right(rect), top(rect)), | |
Point3(ix, left(rect), top(rect)), | |
Point3(ix, left(rect), bottom(rect)), | |
] | |
end, color = colors[1], show_axis = true) | |
lines!(scene3d, lift(iy) do iy | |
rect = Rect(volbb.origin[1:2:3]..., volbb.widths[1:2:3]...) | |
points = [ | |
Point3(left(rect), iy, bottom(rect)), | |
Point3(right(rect), iy, bottom(rect)), | |
Point3(right(rect), iy, top(rect)), | |
Point3(left(rect), iy, top(rect)), | |
Point3(left(rect), iy, bottom(rect)), | |
] | |
end, color = colors[2], show_axis = true) | |
lines!(scene3d, lift(iz) do iz | |
rect = Rect(volbb.origin[1:2]..., volbb.widths[1:2]...) | |
points = [ | |
Point3(left(rect), bottom(rect), iz), | |
Point3(right(rect), bottom(rect), iz), | |
Point3(right(rect), top(rect), iz), | |
Point3(left(rect), top(rect), iz), | |
Point3(left(rect), bottom(rect), iz), | |
] | |
end, color = colors[3], show_axis = true) | |
on(isorangeslider.value) do val | |
vol.isorange[] = val | |
end | |
cameracontrols(scene3d).near[] = 0.1 | |
cameracontrols(scene3d).rotationspeed[] = 0.03 | |
scene | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment