extern crate libc;
extern crate image;
extern crate rayon;
#[macro_use] extern crate itertools;
use std::ptr;
use std::fs::File;
use std::path::Path;
use std::ffi::{CStr, OsStr};
use std::{slice, str, mem};
use rayon::prelude::*;
use libc::{size_t, c_char, c_uchar};
use image::{GenericImage, ImageBuffer, imageops, FilterType, ColorType, ImageDecoder};
mod lazy_load;
#[repr(C)]
#[derive(Clone)]
pub struct Crop {
pub path: *const c_char,
pub crop: *const f32
}
#[repr(C)]
#[derive(Clone)]
pub struct Array {
data: *const libc::c_void,
len: libc::size_t,
}
unsafe impl Send for Array {}
impl Array {
fn from_vec<T>(mut v: Vec<T>) -> Array {
v.shrink_to_fit();
let a = Array {
data: v.as_ptr() as *const libc::c_void,
len: v.len(),
};
mem::forget(v);
a
}
fn into_vec(self) -> Vec<u8> {
unsafe { Vec::from_raw_parts(self.data as *mut u8, self.len, self.len) }
}
}
fn scale_range(val: f32, newmin: f32, newmax: f32) -> f32 {
(((val) * (newmax - newmin)) / (1.0)) + newmin
}
#[no_mangle]
pub fn lazy_crop_and_resize(path: &str, scale: f32, x_crop: f32, y_crop: f32,
max_img_percent: f32, resize_width: u32, resize_height: u32) -> image::DynamicImage
{
assert!(x_crop >= 0f32 && x_crop <= 1f32, "x of crop not bounded in [0, 1]");
assert!(y_crop >= 0f32 && y_crop <= 1f32, "y of crop not bounded in [0, 1]");
let img_size = lazy_load::dimensions(path).unwrap();
let mut x = scale_range(x_crop, 0f32, img_size.0 as f32) as u32;
let mut y = scale_range(y_crop, 0f32, img_size.1 as f32) as u32;
let crop_scale = scale.min(max_img_percent);
let crop_size = ((img_size.0 as f32 * crop_scale).floor().max(2.0) as u32,
(img_size.1 as f32 * crop_scale).floor().max(2.0) as u32);
let max_coords = (img_size.0 - crop_size.0,
img_size.1 - crop_size.1);
x = x.min(max_coords.0);
y = y.min(max_coords.1);
lazy_load::lazy_crop_to_image(path, x, y, crop_size.0, crop_size.1).unwrap().resize_exact(
resize_width, resize_height, FilterType::Nearest
)
}
#[no_mangle]
pub fn crop_and_resize(path: &str, scale: f32, x_crop: f32, y_crop: f32,
max_img_percent: f32, resize_width: u32, resize_height: u32) -> image::DynamicImage
{
assert!(x_crop >= 0f32 && x_crop <= 1f32, "x of crop not bounded in [0, 1]");
assert!(y_crop >= 0f32 && y_crop <= 1f32, "y of crop not bounded in [0, 1]");
let mut img = image::open(&Path::new(&path)).unwrap();
let img_size = img.dimensions();
let mut x = scale_range(x_crop, 0f32, img_size.0 as f32) as u32;
let mut y = scale_range(y_crop, 0f32, img_size.1 as f32) as u32;
let crop_scale = scale.min(max_img_percent);
let crop_size = ((img_size.0 as f32 * crop_scale).floor().max(2.0) as u32,
(img_size.1 as f32 * crop_scale).floor().max(2.0) as u32);
let max_coords = (img_size.0 - crop_size.0,
img_size.1 - crop_size.1);
x = x.min(max_coords.0);
y = y.min(max_coords.1);
img.crop(x, y, crop_size.0, crop_size.1).resize_exact(
resize_width, resize_height, FilterType::Nearest
)
}
#[no_mangle]
pub extern "C" fn parallel_crop_and_resize(image_paths_ptr: *const *const c_char,
return_ptr: *mut u8,
scale_ptr: *const f32,
x_ptr: *const f32,
y_ptr: *const f32,
window_size: u32,
chans: u32,
max_img_percent: f32,
length: size_t)
{
assert!(!scale_ptr.is_null(), "can't operate over null scale vector");
assert!(!x_ptr.is_null(), "can't operate over null x vector");
assert!(!y_ptr.is_null(), "can't operate over null y vector");
assert!(!return_ptr.is_null(), "can't operate over null result vector");
assert!(!image_paths_ptr.is_null(), "can't operate over null list of image paths");
let image_paths_vec: Vec<&str> = unsafe { slice::from_raw_parts(image_paths_ptr, length as usize) }.iter()
.map(|&p| unsafe { CStr::from_ptr(p) }) .map(|cs| cs.to_bytes()) .map(|bs| str::from_utf8(bs).unwrap()) .collect();
let scale_values = unsafe { slice::from_raw_parts(scale_ptr, length as usize) };
let x_values = unsafe { slice::from_raw_parts(x_ptr, length as usize) };
let y_values = unsafe { slice::from_raw_parts(y_ptr, length as usize) };
let mut resultant_vec = vec![];
image_paths_vec.into_par_iter().zip(scale_values)
.zip(x_values.par_iter()).zip(y_values)
.map(|(((path, scale), x), y)| {
Array::from_vec(crop_and_resize(path,
*scale, *x, *y,
max_img_percent,
window_size,
window_size).raw_pixels()
)
}).collect_into_vec(&mut resultant_vec);
let win_size = (window_size * window_size * chans) as usize;
for (begin, rvec) in izip!((0..length*win_size).step_by(win_size), resultant_vec)
{
assert!(rvec.len == win_size, "rvec [{:?}] != window_size [{:?}]", rvec.len, win_size);
unsafe { ptr::copy(rvec.data as *const u8, return_ptr.offset(begin as isize),
win_size) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bw_image_crop() {
let center = crop_and_resize("assets/lena_gray.png",
0.25, 0.5, 0.5, 0.25, 32, 32);
println!("dims: {:?}", center.dimensions());
center.save(&Path::new("assets/test_lena_gray_center.png")).unwrap();
assert!(center.dimensions() == (32, 32));
let is_color = match center.color() {
ColorType::Gray(_) => false,
_ => true
};
assert!(is_color == false);
}
#[test]
fn test_image_crops() {
let top_left = crop_and_resize("assets/lena.png",
0.25, 0.25, 0.25, 0.25, 32, 32);
println!("dims: {:?}", top_left.dimensions());
top_left.save(&Path::new("assets/test_lena_top_left.png")).unwrap();
assert!(top_left.dimensions() == (32, 32), "top left was {:?}", top_left.dimensions());
let top_right = crop_and_resize("assets/lena.png",
0.25, 0.75, 0.25, 0.25, 32, 32);
top_right.save(&Path::new("assets/test_lena_top_right.png")).unwrap();
assert!(top_right.dimensions() == (32, 32), "top right was {:?}", top_right.dimensions());
let bottom_left = crop_and_resize("assets/lena.png",
0.25, 0.25, 0.75, 0.25, 32, 32);
bottom_left.save(&Path::new("assets/test_lena_bottom_left.png")).unwrap();
assert!(bottom_left.dimensions() == (32, 32), "bottom left was {:?}", bottom_left.dimensions());
let bottom_right = crop_and_resize("assets/lena.png",
0.25, 0.75, 0.75, 0.25, 32, 32);
bottom_right.save(&Path::new("assets/test_lena_bottom_right.png")).unwrap();
assert!(bottom_right.dimensions() == (32, 32), "bottom right was {:?}", bottom_right.dimensions());
let center = crop_and_resize("assets/lena.png",
0.25, 0.5, 0.5, 0.25, 32, 32);
center.save(&Path::new("assets/test_lena_center.png")).unwrap();
assert!(center.dimensions() == (32, 32), "center right was {:?}", center.dimensions());
}
}