1use std::fs::File;
9use std::io::{BufRead, BufReader, Write, LineWriter};
10use std::path::{Path};
11use std::fmt;
12
13
14use crate::error::{NeuroformatsError, Result};
15use crate::util::vec32minmax;
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct FsLabel {
19 pub vertexes: Vec<FsLabelVertex>,
20}
21
22impl FsLabel {
23
24 pub fn is_binary(&self) -> bool {
35 let mut values_iter = self.vertexes.iter().map(|x| x.value);
36 let first_val = values_iter.next().expect("Empty label");
37 values_iter.all(|val| val == first_val)
38 }
39
40
41 pub fn is_surface_vertex_in_label(&self, num_surface_verts: usize) -> Vec<bool> {
50 if num_surface_verts < self.vertexes.len() {
51 panic!("Invalid vertex count 'num_surface_verts' for surface: label contains {} vertices, surface cannot contain only {}.", self.vertexes.len(), num_surface_verts);
53 }
54 let mut data_bin = vec![false; num_surface_verts];
55 for label_vert in self.vertexes.iter() {
56 data_bin[label_vert.index as usize] = true;
57 }
58 data_bin
59 }
60
61
62 pub fn as_surface_data(&self, num_surface_verts : usize, not_in_label_value : f32) -> Vec<f32> {
71 let mut surface_data : Vec<f32> = vec![not_in_label_value; num_surface_verts];
72 for surface_vert in self.vertexes.iter() {
73 surface_data[surface_vert.index as usize] = surface_vert.value;
74 }
75 surface_data
76 }
77}
78
79impl fmt::Display for FsLabel {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 let (min, max) = vec32minmax(self.vertexes.iter().map(|v| v.value), false);
82 write!(f, "Label for {} vertices/voxels, with label values in range {} to {}.", self.vertexes.len(), min, max)
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub struct FsLabelVertex {
88 pub index: i32,
89 pub coord1: f32,
90 pub coord2: f32,
91 pub coord3: f32,
92 pub value: f32,
93}
94
95impl std::str::FromStr for FsLabelVertex {
97 type Err = NeuroformatsError;
98 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
99 let mut iter = s.split_whitespace();
100 let index = iter.next().unwrap().parse::<i32>().expect("Expected vertex index of type i32.");
101 let coord1 = iter.next().unwrap().parse::<f32>().expect("Expected coord1 of type f32.");
102 let coord2 = iter.next().unwrap().parse::<f32>().expect("Expected coord2 of type f32.");
103 let coord3 = iter.next().unwrap().parse::<f32>().expect("Expected coord3 of type f32.");
104 let value = iter.next().unwrap().parse::<f32>().expect("Expected vertex value of type f32.");
105 Ok(FsLabelVertex{ index, coord1, coord2, coord3, value })
106 }
107}
108
109pub fn read_label<P: AsRef<Path>>(path: P) -> Result<FsLabel> {
122
123 let reader = BufReader::new(File::open(path)?);
124
125 let mut lines = reader.lines();
127 let _comment_line = lines.next().transpose()?;
129 let hdr_num_entries: i32 = lines.next().transpose()?.and_then(|header| header.parse::<i32>().ok()).expect("Could not parse label header line.");
131 let mut vertexes = Vec::with_capacity(hdr_num_entries as usize);
132 for line in lines {
133 let line = line?;
134 let vertex = line.parse()?;
135 vertexes.push(vertex);
136 }
137
138 if hdr_num_entries as usize != vertexes.len() {
139 Err(NeuroformatsError::InvalidFsLabelFormat)
140 } else {
141 Ok(FsLabel{ vertexes })
142 }
143}
144
145
146pub fn write_label<P: AsRef<Path> + Copy>(path: P, label : &FsLabel) -> std::io::Result<()> {
148 let file = File::create(path)?;
149 let mut file = LineWriter::new(file);
150
151 let header_lines = format!("# FreeSurfer label.\n{}\n", label.vertexes.len());
152 let header_lines = header_lines.as_bytes();
153 file.write_all(header_lines)?;
154
155 for vertex in label.vertexes.iter() {
156 let vline = format!("{} {} {} {} {}\n", vertex.index, vertex.coord1, vertex.coord2, vertex.coord3, vertex.value);
157 let vline = vline.as_bytes();
158 file.write_all(vline)?;
159 }
160
161 file.flush()?;
162
163 Ok(())
164}
165
166
167#[cfg(test)]
168mod test {
169 use super::*;
170 use tempfile::{tempdir};
171
172 #[test]
173 fn the_demo_surface_label_file_can_be_read() {
174 const LABEL_FILE: &str = "resources/subjects_dir/subject1/label/lh.entorhinal_exvivo.label";
175 let label = read_label(LABEL_FILE).unwrap();
176
177 let expected_vertex_count: usize = 1085;
178 assert_eq!(expected_vertex_count, label.vertexes.len());
179 }
180
181 #[test]
182 fn the_label_utility_functions_work() {
183 const LABEL_FILE: &str = "resources/subjects_dir/subject1/label/lh.entorhinal_exvivo.label";
184 let label = read_label(LABEL_FILE).unwrap();
185
186 let num_surface_verts: usize = 160_000;
187 let label_mask = label.is_surface_vertex_in_label(num_surface_verts);
188 assert_eq!(num_surface_verts, label_mask.len());
189
190 let surface_data = label.as_surface_data(num_surface_verts, f32::NAN);
191 assert_eq!(num_surface_verts, surface_data.len());
192
193 assert_eq!(false, label.is_binary());
194 }
195
196 #[test]
197 fn a_label_file_can_be_written_and_reread() {
198 const LABEL_FILE: &str = "resources/subjects_dir/subject1/label/lh.entorhinal_exvivo.label";
199 let label = read_label(LABEL_FILE).unwrap();
200
201 let dir = tempdir().unwrap();
202
203 let tfile_path = dir.path().join("temp-file.label");
204 let tfile_path = tfile_path.to_str().unwrap();
205 write_label(tfile_path, &label).unwrap();
206
207 let label_re = read_label(tfile_path).unwrap();
208 let expected_vertex_count: usize = 1085;
209 assert_eq!(expected_vertex_count, label_re.vertexes.len());
210 }
211
212}