-
Notifications
You must be signed in to change notification settings - Fork 0
/
gif.rs
72 lines (59 loc) · 1.85 KB
/
gif.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use std::io::{BufRead, Seek};
use image::{codecs::gif::GifDecoder, AnimationDecoder};
use crate::{examine, model::Classification, Model, Result};
/// An iterator that examines every frame of a GIF.
///
/// ### Example
/// ```rust,ignore
/// let gif_path = concat!(env!("CARGO_MANIFEST_DIR"), "/media/test_porn.gif");
/// let file = BufReader::new(File::open(gif_path)?);
/// let gif = GifParser::new(GifDecoder::new(file)?, &model);
///
/// for (index, frame) in gif.enumerate() {
/// println!("frame {index}: {frame:#?}");
/// }
/// ```
///
/// ### Example: Not iterating
/// ```rust, ignore
/// let gif = GifParser::new(GifDecoder::new(file)?, &model);
/// let frames = gif.collect::<Vec<_>>();
/// ```
pub struct GifParser<'a> {
frames: image::Frames<'a>,
model: &'a Model,
}
impl<'a> GifParser<'a> {
pub fn new<R: BufRead + Seek + 'a>(gif: GifDecoder<R>, model: &'a Model) -> Self {
let frames = gif.into_frames();
Self { frames, model }
}
}
impl<'a> Iterator for GifParser<'a> {
type Item = Result<Vec<Classification>>;
fn next(&mut self) -> Option<Self::Item> {
let frame = self.frames.next();
match frame {
Some(Ok(frame)) => Some(examine(self.model, &frame.into_buffer())),
Some(Err(e)) => Some(Err(e.into())),
None => None,
}
}
}
#[cfg(test)]
mod test {
use crate::MODEL;
use image::codecs::gif::GifDecoder;
use std::{fs::File, io::BufReader};
use super::{GifParser, Result};
#[tokio::test]
async fn test_gif() -> Result<()> {
let gif_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test/puffBounce.gif");
let file = BufReader::new(File::open(gif_path)?);
let frames = GifParser::new(GifDecoder::new(file)?, &MODEL);
for frame in frames {
assert!(frame.is_ok());
}
Ok(())
}
}