1 |
(* image-info.sml |
(* image-info.sml |
2 |
* |
* |
3 |
* COPYRIGHT (c) 2010 The Diderot Project (http://diderot-language.cs.uchicago.edu) |
* COPYRIGHT (c) 2012 The Diderot Project (http://diderot-language.cs.uchicago.edu) |
4 |
* All rights reserved. |
* All rights reserved. |
5 |
* |
* |
6 |
* Information about a NRRD file. |
* Information about an image object. |
7 |
* |
* |
8 |
* TODO: |
* TODO: |
9 |
* handle images where the symmetries are exploited. |
* handle images where the symmetries are exploited. |
11 |
|
|
12 |
structure ImageInfo : sig |
structure ImageInfo : sig |
13 |
|
|
14 |
(* Image voxels are tensors of some raw representation type *) |
type info |
|
type voxel_ty = (int list * RawTypes.ty) |
|
|
|
|
|
datatype info = ImgInfo of { |
|
|
id : OS.FileSys.file_id, (* ID of image file *) |
|
|
dim : int, (* dimension of space *) |
|
|
ty : voxel_ty, (* types of image samples *) |
|
|
origin : FloatLit.float list, (* center of first sample *) |
|
|
sizes : int list (* number of samples along each axis; |
|
|
* we follow the Nrrd convention of |
|
|
* listing the axes in fast to slow |
|
|
* order. |
|
|
*) |
|
|
} |
|
15 |
|
|
16 |
(* are the underlying files the same? *) |
(* are the underlying files the same? *) |
17 |
val same : info * info -> bool |
val same : info * info -> bool |
19 |
(* hash value (based on image file ID) *) |
(* hash value (based on image file ID) *) |
20 |
val hash : info -> word |
val hash : info -> word |
21 |
|
|
22 |
(* get image info from a Nrrd file *) |
(* make image info from a proxy Nrrd file, target domain dimension, and range shape; |
23 |
val getInfo : string -> info |
* return NONE if the domain and/or range do not match the structure of the nrrd file. |
24 |
|
*) |
25 |
|
val fromNrrd : NrrdInfo.info * int * int list -> info option |
26 |
|
|
27 |
|
(* create a default image from a domain dimension and range shape. *) |
28 |
|
val mkInfo : int * int list -> info |
29 |
|
|
30 |
val toString : info -> string |
val toString : info -> string |
31 |
val dim : info -> int (* dimension of space *) |
val dim : info -> int (* dimension of space *) |
32 |
val sizes : info -> int list (* size of each dimension *) |
val sizes : info -> int list (* size of each dimension (not including the data axis) *) |
33 |
val voxelShape : info -> int list (* shape of voxels; empty list for scalars *) |
val voxelShape : info -> int list (* shape of voxels; empty list for scalars *) |
34 |
val voxelSzB : info -> int (* size in bytes of a voxel *) |
val voxelSzB : info -> int (* size in bytes of a voxel *) |
35 |
val stride : info -> int (* for non-scalar images, this returns the *) |
val stride : info -> int (* for non-scalar images, this returns the *) |
38 |
|
|
39 |
end = struct |
end = struct |
40 |
|
|
41 |
(* Image samples are tensors of some raw representation type *) |
(* Image voxels are tensors of some raw representation type *) |
42 |
type voxel_ty = (int list * RawTypes.ty) |
type voxel_ty = (int list * RawTypes.ty) |
43 |
|
|
44 |
datatype info = ImgInfo of { |
datatype info = ImgInfo of { |
45 |
id : OS.FileSys.file_id, (* ID of image file *) |
stamp : Stamp.stamp, (* unique ID *) |
46 |
dim : int, (* dimension of space *) |
dim : int, (* dimension of space *) |
47 |
ty : voxel_ty, (* types of image samples *) |
sizes : int list, (* number of samples along each axis (not including |
48 |
origin : FloatLit.float list, (* center of first sample *) |
* the data axis); we follow the Nrrd convention of |
49 |
sizes : int list (* number of samples along each axis; |
* listing the axes in fast to slow order. |
|
* we follow the Nrrd convention of |
|
|
* listing the axes in fast to slow |
|
|
* order. |
|
50 |
*) |
*) |
51 |
|
ty : voxel_ty |
52 |
} |
} |
53 |
|
|
54 |
fun same (ImgInfo{id=a, ...}, ImgInfo{id=b, ...}) = (a = b) |
fun same (ImgInfo{stamp=s1, ...}, ImgInfo{stamp=s2, ...}) = Stamp.same(s1, s2) |
55 |
|
fun hash (ImgInfo{stamp, ...}) = Stamp.hash stamp |
|
fun hash (ImgInfo{id, ...}) = OS.FileSys.hash id |
|
56 |
|
|
57 |
fun doHeader (fileName, header) = let |
(* make image info from a proxy Nrrd file, target domain dimension, and range shape *) |
58 |
fun fields s = String.tokens Char.isSpace s |
fun fromNrrd (nrrd, dim, dd) = let |
59 |
fun set (r, v) = (r := SOME v) |
val {elemTy, nElems, ...} = NrrdInfo.voxelInfo nrrd |
60 |
fun get (tag, r) = (case !r of NONE => raise Fail("missing "^tag) | SOME v => v) |
val nElems' = List.foldl (op * ) 1 dd |
|
val ty = ref NONE |
|
|
val totalDim = ref NONE |
|
|
val dim = ref NONE |
|
|
val sizes = ref NONE |
|
|
fun doValue ("type", v) = set(ty, RawTypes.fromString v) |
|
|
| doValue ("dimension", v) = set (totalDim, valOf(Int.fromString v)) |
|
|
| doValue ("space dimension", v) = set (dim, valOf(Int.fromString v)) |
|
|
| doValue ("sizes", v) = let |
|
|
fun atoi s = (case Int.fromString s |
|
|
of SOME i => i |
|
|
| NONE => raise Fail(concat[ |
|
|
"bogus dnorm output: \"", "sizes: ", String.toString v, "\n" |
|
|
]) |
|
|
(* end case *)) |
|
61 |
in |
in |
62 |
set (sizes, List.map atoi (fields v)) |
if (NrrdInfo.dim nrrd <> dim) orelse (nElems <> nElems') |
63 |
end |
then NONE |
64 |
| doValue ("space directions", v) = () |
else SOME(ImgInfo{ |
65 |
| doValue ("kinds", v) = () |
stamp = Stamp.new(), |
|
| doValue ("endian", v) = () |
|
|
| doValue ("encoding", v) = () |
|
|
| doValue ("space origin", v) = () |
|
|
| doValue _ = () |
|
|
in |
|
|
Log.msg (concat[fileName, " file header:\n"]); |
|
|
List.app (fn (tag, value) => Log.msg(concat[" ", tag, ": ", value, "\n"])) header; |
|
|
List.app doValue header; |
|
|
let |
|
|
val dim = get ("space dimension", dim) |
|
|
val rngDim = get ("dimension", totalDim) - dim |
|
|
(* split the sizes into those that control the shape of the voxels, which come first |
|
|
* in the list, and those that span the image's dimensions. |
|
|
*) |
|
|
val (rngShape, sizes) = let |
|
|
fun split (0, l, prefix) = (List.rev prefix, l) |
|
|
| split (i, d::l, prefix) = split(i-1, l, d::prefix) |
|
|
| split _ = raise Fail "bogus dnorm output: too few sizes" |
|
|
in |
|
|
split (rngDim, get ("sizes", sizes), []) |
|
|
end |
|
|
in |
|
|
ImgInfo{ |
|
|
id = OS.FileSys.fileId fileName, |
|
66 |
dim = dim, |
dim = dim, |
67 |
ty = (rngShape, get ("type", ty)), |
sizes = NrrdInfo.sizes nrrd, |
68 |
origin = [], (* FIXME *) |
ty = (dd, elemTy) |
69 |
sizes = sizes |
}) |
|
} |
|
|
end |
|
70 |
end |
end |
71 |
|
|
72 |
fun getInfo fileName = if OS.FileSys.access(fileName, [OS.FileSys.A_READ]) |
fun mkInfo (dim, dd) = ImgInfo{ |
73 |
then let |
stamp = Stamp.new(), |
74 |
val {version, header} = RunDNorm.run fileName |
dim = dim, |
75 |
in |
sizes = [], (* unknown *) |
76 |
doHeader (fileName, header) |
ty = (dd, !RawTypes.realTy) |
77 |
end |
} |
|
else raise Fail(concat["Nrrd file \"", fileName, "\" does not exist"]) |
|
78 |
|
|
79 |
fun toString (ImgInfo{dim, ty=(dd, rTy), ...}) = let |
fun toString (ImgInfo{dim, ty=(dd, rTy), ...}) = let |
80 |
val shape = (case dd |
val shape = (case dd |