Skip to content

Instantly share code, notes, and snippets.

@jkrumbiegel
Last active December 21, 2019 00:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkrumbiegel/bd98cf311f94b589272ad8c0e0ca60b3 to your computer and use it in GitHub Desktop.
Save jkrumbiegel/bd98cf311f94b589272ad8c0e0ca60b3 to your computer and use it in GitHub Desktop.
Makie brain viewer
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