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
extern crate image;

use ndarray::*;
use image::{ImageDecoder, DecodingResult, ColorType};
use string_error::{static_err};
use std::error::Error;
use std::io::{Read, Write, Cursor};

pub fn convolve2d(base_arr: &Array2<f32>, filter: &Array2<f32>) -> Array2<f32> {
    let ys: isize = base_arr.shape()[0] as isize;
    let xs: isize = base_arr.shape()[1] as isize;
    let mut result: Array2<f32> = Array2::<f32>::zeros((ys as usize, xs as usize));
    for yt in 1..((result.shape()[0]-1) as isize) {
        for xt in 1..((result.shape()[1]-1) as isize) {
            result[[yt as usize, xt as usize]] = (&base_arr.slice(s![yt-1..yt+2, xt-1..xt+2]) * filter).scalar_sum();
        }
    }
    return result;
}

pub fn read_image<R: Read>(mut image_file: R) -> Result<Array3<f32>, Box<dyn Error>> {
    let mut image_buffer = Vec::new();
    image_file.read_to_end(&mut image_buffer)?;
    let format = image::guess_format(&image_buffer)?;
    let image_reader = Cursor::new(image_buffer);
    match format {
        image::ImageFormat::PNG => read_to_array(image::png::PNGDecoder::new(image_reader)),
        image::ImageFormat::JPEG => read_to_array(image::jpeg::JPEGDecoder::new(image_reader)),
        _ => Err(static_err("Unsupported file type. Only PNG and JPEG are supported.")),
    }
}

fn read_to_array<D: ImageDecoder>(mut decoder: D) -> Result<Array3<f32>, Box<dyn Error>> {
    let result = decoder.read_image()?;
    let (x, y) = decoder.dimensions()?;
    let colortype = decoder.colortype()?;
    
    let raw_data = match result {
        DecodingResult::U8(v) => v.into_iter().map(|e| e as f32).collect::<Vec<f32>>(),
        DecodingResult::U16(v) => v.into_iter().map(|e| e as f32).collect::<Vec<f32>>(),
    };
    let arr = Array1::<f32>::from(raw_data);

    match colortype {
        ColorType::RGBA(_) =>
            arr.into_shape((y as Ix, x as Ix, 4 as Ix)).map_err(|_| static_err("Wrong shape!")),
        ColorType::RGB(_) =>
            arr.into_shape((y as Ix, x as Ix, 3 as Ix)).map_err(|_| static_err("Wrong shape!")),
        _ => Err(static_err("Unsupported colortype")),
    }
}

pub fn write_grayscale_png(image_file: Box<dyn Write>, img: &Array2<f32>) -> Result<(), Box<dyn Error>> {
    let decoder = image::png::PNGEncoder::new(image_file);
    let shape = img.shape();
    let height = shape[0] as u32;
    let width = shape[1] as u32;
    let u8img = img.clone().map(|e| *e as u8);
    let data = u8img.as_slice().unwrap();
    decoder.encode(
        data,
        width, height, ColorType::Gray(8)
    ).map_err(|_| static_err("Error writing image!"))
}