Skip to content

Commit

Permalink
Added a scan_timestamps method, only marginally faster so far... wi…
Browse files Browse the repository at this point in the history
…ll think on it
  • Loading branch information
StephenThornquist committed Jul 4, 2024
1 parent 3063121 commit 0916346
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 32 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ criterion = { version = "0.5", features = ["html_reports"] }
name = "opening_files_benchmark"
harness = false

[[bench]]
name = "read_frames_benchmark"
harness = false
# [[bench]]
# name = "read_frames_benchmark"
# harness = false

# [[bench]]
# name = "mask_frames_benchmark"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ TODOS:

- `mask` methods for `tau_d` or for `.tiff` files.

- Fast timestamp checking without reading as much metadata?
Make a `scan_timestamps` method.

- `get_epoch_timestamps_both` doesn't error if `system` timestamps
don't exist! It will just crash! Because I don't use the `?` correctly.

Expand Down
10 changes: 10 additions & 0 deletions benches/metadata_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ fn criterion_benchmark_frame_metadata(c: &mut Criterion) {
},
);

read_bench.bench_with_input(
BenchmarkId::new("Scan timestamps",
0,
),
&(),
|bench, _| {
bench.iter(|| black_box(corrosiff::SiffReader::scan_timestamps(LONG_SIFF_PATH).unwrap()))
},
);

read_bench.bench_with_input(
BenchmarkId::new("Get 40 experiment timestamps",
40,
Expand Down
12 changes: 12 additions & 0 deletions benches/opening_files_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ fn criterion_benchmark(c: &mut Criterion) {
|b| b.iter(|| black_box(open_short_siff_test()))
);
c.bench_function("open a long siff file", |b| b.iter(|| black_box(open_long_siff_test())));

c.bench_function("Scan timestamps for short siff",
|b| b.iter(|| black_box(corrosiff::SiffReader::scan_timestamps(
"/Users/stephen/Desktop/Data/imaging/2024-04/2024-04-07/Dh31_LexA_LKir_LGFlamp1/Fly1/BarOnAtTen_1.siff"
).unwrap()))
);

c.bench_function("Scan timestamps for long siff",
|b| b.iter(|| black_box(corrosiff::SiffReader::scan_timestamps(
"/Users/stephen/Desktop/Data/imaging/2024-04/2024-04-17/21Dhh_GCaFLITS/Fly1/Flashes_1.siff"
).unwrap()))
);
}

criterion_group!(benches, criterion_benchmark);
Expand Down
39 changes: 39 additions & 0 deletions src/siffreader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,34 @@ impl SiffReader{
)
}

/// A fast parse that returns just the first and last timestamps in the file.
/// Uses the system timestamps.
pub fn scan_timestamps<P: AsRef<Path>>(filename: P) -> Result<(u64, u64), CorrosiffError> {
let file = File::open(&filename)?;
let mut buff = BufReader::new(&file);
let file_format = {
FileFormat::minimal_filetype(&mut buff)
.map_err(|e| CorrosiffError::FileFormatError)
}?;

let mut wee_buff = BufReader::with_capacity(400, &file);
//let mut end_buff =
let mut ifds = file_format.get_ifd_iter(&mut wee_buff);
// let mut ifd_vec = file_format.get_ifd_iter(&mut wee_buff).collect::<Vec<_>>();
let (first, last) = (
get_epoch_timestamps_system(
&[&ifds.next().ok_or_else(||CorrosiffError::FileFormatError)?],
&mut ifds.reader
)?[0].unwrap(),
get_epoch_timestamps_system(
&[&ifds.last().ok_or_else(||CorrosiffError::FileFormatError)?],
&mut wee_buff
)?[0].unwrap()
);

Ok((first, last))
}

/// Returns number of frames in the file
/// (including flyback etc).
pub fn num_frames(&self) -> usize {
Expand Down Expand Up @@ -2326,6 +2354,17 @@ mod tests {
assert!(reader.is_ok());
}

#[test]
fn test_scan_timestamps(){
let timestamps = SiffReader::scan_timestamps(TEST_FILE_PATH);
assert!(timestamps.is_ok());
let (start, end) = timestamps.unwrap();
assert!(start <= end);
assert_ne!(start, 0);
assert_ne!(end, 0);
println!("Start: {}, End: {}", start, end);
}

#[test]
fn read_frame() {
let reader = SiffReader::open(TEST_FILE_PATH).unwrap();
Expand Down
20 changes: 20 additions & 0 deletions src/tiff/file_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,26 @@ impl FileFormat{
})
}

/// Just reads and stores enough to determine how to find IFDs.
pub fn minimal_filetype<'a, 'b, T>(buffer : &'a mut T) -> Result<Self, String>
where T : Read + Seek {
let siff_header = SiffHeader::read(buffer)
.map_err(
|err| format!("Error reading header: {}", err)
)?;
Ok(
FileFormat {
_tiff_type : match &siff_header.tiffheader {
TiffHeader::Default {..} => TiffType::Tiff,
TiffHeader::BigTiff {..} => TiffType::BigTiff,
},
siff_header,
nvfd : String::new(),
roi_string : String::new(),
}
)
}

/// Returns the location of the first IFD
/// in the file (from start).
///
Expand Down
76 changes: 47 additions & 29 deletions src/tiff/ifd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,36 +246,54 @@ pub struct IFDIterator<'reader, S, T> where S : SeekRead , T : IFD {

impl <'a, S, IFDT> IFDIterator<'a, S, IFDT>
where S : SeekRead, IFDT : IFD,
for<'args> <() as BinRead>::Args<'args> : Default {

/// TODO - Implement and test!! The generic needs to take in the
/// IFD type?
///
/// Creates a new `IFDIterator` object from an object that
/// can read and seek and the location of the first IFD (so that
/// it can parse it and find the subsequent IFDs).
///
/// ## Arguments
///
/// * `reader` - A reader that can read and seek
/// * `first_ifd` - The location of the first IFD in the file
///
/// ## Returns
///
/// * `IFDIterator` - An iterator object that can be used to read
///
/// ## Example
///
/// ```rust, ignore
/// ```
#[allow(dead_code)]
fn new(reader : &'a mut S, first_ifd : IFDT::PointerSize) -> Self {
IFDIterator{
reader,
to_next : first_ifd,
}
for<'args> <IFDT as BinRead>::Args<'args> : Default {

/// TODO - Implement and test!! The generic needs to take in the
/// IFD type?
///
/// Creates a new `IFDIterator` object from an object that
/// can read and seek and the location of the first IFD (so that
/// it can parse it and find the subsequent IFDs).
///
/// ## Arguments
///
/// * `reader` - A reader that can read and seek
/// * `first_ifd` - The location of the first IFD in the file
///
/// ## Returns
///
/// * `IFDIterator` - An iterator object that can be used to read
///
/// ## Example
///
/// ```rust, ignore
/// ```
#[allow(dead_code)]
fn new(reader : &'a mut S, first_ifd : IFDT::PointerSize) -> Self {
IFDIterator{
reader,
to_next : first_ifd,
}

}

/// Returns the last IFD before the actual last IFD -- i.e. the
/// IFD guaranteed to have a full frame and metadata after it.
///
/// ## Returns
///
/// * `Option<IFDT>` - The last IFD in the file corresponding
/// to a complete frame. Returns `None` if the file has only one
/// complete IFD.
///
pub fn last_complete(&mut self) -> Option<<IFDIterator<'a, S, IFDT> as Iterator>::Item> {
let mut back_two = self.next()?;
let mut back_one = self.next()?;
while let Some(next) = self.next() {
back_two = back_one;
back_one = next;
}
Some(back_two)
}
}

impl<'a, S, IFDT> Iterator for IFDIterator<'a, S, IFDT>
Expand Down

0 comments on commit 0916346

Please sign in to comment.