-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
409 additions
and
66 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Illustrates how to load a FreeSurfer brain mesh and export it to Wavefront Object format. | ||
# You can open the result in Blender or other standard 3D modeling software. | ||
|
||
using NeuroFormats | ||
|
||
BRAIN_MESH_FILE = joinpath(ENV["HOME"], "develop/NeuroFormats.jl/test/data/subjects_dir/subject1/surf/lh.white") | ||
surface = read_fs_surface(BRAIN_MESH_FILE) | ||
export_to_obj(joinpath(ENV["HOME"], "brain.obj"), surface.mesh) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module FreeSurfer | ||
|
||
using Printf | ||
|
||
export read_fs_int24, interpret_fs_int24, read_curv, write_curv, read_fs_surface, num_vertices, num_faces, export_to_obj | ||
|
||
include("./utils.jl") | ||
include("./fs_common.jl") | ||
include("./fs_curv.jl") | ||
include("./fs_surface.jl") | ||
|
||
end |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Common functions for reading FreeSurfer data files. | ||
|
||
import Base.hton, Base.ntoh | ||
|
||
|
||
function read_fs_int24(io::IO; endian::AbstractString = "little") | ||
if ! (endian in ["little", "big"]) | ||
error("Parameter 'endian' must be one of 'little' or 'big'.") | ||
end | ||
|
||
sub_values::Array{Int64,1} = zeros(3) | ||
endian_func = (endian == "little" ? ltoh : ntoh) | ||
|
||
b1::Int64 = Int64(endian_func(read(io, UInt8))) | ||
b2::Int64 = Int64(endian_func(read(io, UInt8))) | ||
b3::Int64 = Int64(endian_func(read(io, UInt8))) | ||
fs_int24 = b1 << 16 + b2 << 8 + b3 | ||
return fs_int24 | ||
end | ||
|
||
|
||
""" Interpret 3 single-byte unsigned integers as a single integer, as used in several FreeSurfer file formats. """ | ||
function interpret_fs_int24(b1::Integer, b2::Integer, b3::Integer) | ||
#@printf("b1=%d, b2=%d, b3=%d (types: %s, %s, %s)\n", b1, b2, b3, typeof(b1), typeof(b2), typeof(b1)); | ||
fs_int24::Int64 = Int64(b1) << 16 + Int64(b2) << 8 + Int64(b3) | ||
return fs_int24 | ||
end | ||
|
||
|
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# Functions for reading FreeSurfer brain surface meshes. | ||
|
||
import Base.show | ||
|
||
const TRIS_MAGIC_FILE_TYPE_NUMBER = 16777214 | ||
|
||
""" Models the header section of a file in FreeSurfer Surface format. The files are big endian. """ | ||
mutable struct FsSurfaceHeader | ||
magic_b1::UInt8 | ||
magic_b2::UInt8 | ||
magic_b3::UInt8 | ||
info_line::AbstractString | ||
num_vertices::Int32 | ||
num_faces::Int32 | ||
end | ||
|
||
num_vertices(sh::FsSurfaceHeader) = sh.num_vertices | ||
num_faces(sh::FsSurfaceHeader) = sh.num_faces | ||
|
||
|
||
""" Models a trimesh. Vertices are defined by their xyz coordinates, and faces are given as indices into the vertex array. """ | ||
struct BrainMesh | ||
vertices::Array{Float32, 2} # vertex xyz coords | ||
faces::Array{Int32, 2} # indices of the 3 vertices forming the face / polygon / triangle | ||
end | ||
|
||
num_vertices(bm::BrainMesh) = Base.length(bm.vertices) / 3 | ||
num_faces(bm::BrainMesh) = Base.length(bm.faces) / 3 | ||
Base.show(io::IO, x::BrainMesh) = @printf("Brain mesh with %d vertices and %d faces.\n", num_vertices(x), num_faces(x)) | ||
|
||
""" Models FreeSurfer Surface file. """ | ||
struct FsSurface | ||
header::FsSurfaceHeader | ||
mesh::BrainMesh | ||
end | ||
|
||
num_vertices(fsf::FsSurface) = num_vertices(fsf.mesh) | ||
num_faces(fsf::FsSurface) = num_faces(fsf.mesh) | ||
Base.show(io::IO, x::FsSurface) = @printf("FreeSurfer brain mesh with %d vertices and %d faces.\n", num_vertices(x), num_faces(x)) | ||
|
||
|
||
""" Read header from a FreeSurfer brain surface file """ | ||
# For fixed length strings, we could do: my_line = bytestring(readbytes(fh, 4)) I guess. | ||
function read_fs_surface_header(io::IO) | ||
header = FsSurfaceHeader(UInt8(hton(read(io, UInt8))), UInt8(hton(read(io, UInt8))), UInt8(hton(read(io, UInt8))), | ||
read_variable_length_string(io), | ||
Int32(hton(read(io, Int32))), Int32(hton(read(io, Int32)))) | ||
magic = interpret_fs_int24(header.magic_b1, header.magic_b2, header.magic_b3) | ||
if magic != TRIS_MAGIC_FILE_TYPE_NUMBER | ||
error("This is not a supported binary FreeSurfer Surface file: header magic code mismatch.") | ||
end | ||
header | ||
end | ||
|
||
|
||
""" | ||
read_fs_surface(file::AbstractString) | ||
Read a brain surface model represented as a mesh from a file in FreeSurfer binary surface format. Such a file typically represents a single hemisphere. | ||
# Examples | ||
```julia-repl | ||
julia> mesh = read_fs_surface("~/study1/subject1/surf/lh.white") | ||
``` | ||
""" | ||
function read_fs_surface(file::AbstractString) | ||
file_io = open(file, "r") | ||
header = read_fs_surface_header(file_io) | ||
|
||
vertices_raw::Array{Float32,1} = reinterpret(Float32, read(file_io, sizeof(Float32) * header.num_vertices * 3)) | ||
vertices_raw .= ntoh.(vertices_raw) | ||
vertices::Array{Float32,2} = Base.reshape(vertices_raw, (3, Base.length(vertices_raw)÷3))' | ||
|
||
faces_raw::Array{Int32,1} = reinterpret(Int32, read(file_io, sizeof(Int32) * header.num_faces * 3)) | ||
faces_raw .= ntoh.(faces_raw) | ||
faces::Array{Int32,2} = Base.reshape(faces_raw, (3, Base.length(faces_raw)÷3))' | ||
|
||
close(file_io) | ||
|
||
surface = FsSurface(header, BrainMesh(vertices, faces)) | ||
surface | ||
end | ||
|
||
|
||
""" Export a brain mesh to a Wavefront Object File. """ | ||
function export_to_obj(file:: AbstractString, bm::BrainMesh) | ||
open(file, "w") do f | ||
for row_idx in 1:size(bm.vertices, 1) | ||
row = bm.vertices[row_idx, :] | ||
vertex_rep = @sprintf("v %f %f %f\n", row[1], row[2], row[3]) | ||
write(f, vertex_rep) | ||
end | ||
for row_idx in 1:size(bm.faces, 1) | ||
row = bm.faces[row_idx, :] | ||
face_rep = @sprintf("f %d %d %d\n", row[1]+1, row[2]+1, row[3]+1) | ||
write(f, face_rep) | ||
end | ||
end | ||
end | ||
|
||
|
||
""" Export the mesh of a FreeSurfer surface to a Wavefront Object File. """ | ||
export_to_obj(file:: AbstractString, x::FsSurface) = export_to_obj(file, x.mesh) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# General utility functions used throughout the project. | ||
|
||
|
||
#https://stackoverflow.com/questions/61264545/read-null-terminated-string-from-byte-vector-in-julia | ||
|
||
|
||
""" Read a variable length, 0-terminated C-style string from a binary file. The trailing zero will be read if consume_zero is set, but not included in the result. """ | ||
function read_variable_length_string(io::IO; consume_zero::Bool = false) | ||
res_string = "" | ||
while (! eof(io)) | ||
char = read(io, UInt8) | ||
if char == 0 | ||
if ! consume_zero | ||
Base.seek(io, Base.position(io) - 1) | ||
end | ||
break | ||
else | ||
res_string = res_string * Char(char) | ||
end | ||
end | ||
res_string | ||
end |
Binary file not shown.
Binary file not shown.
Oops, something went wrong.