use nalgebra::*;
use ripple::signal::sampling::{self};
use std::ops::{Index, IndexMut, Mul, Add, AddAssign, MulAssign, SubAssign};
use simba::scalar::SubsetOf;
use std::fmt;
use std::fmt::Debug;
#[cfg(feature="opencvlib")]
use opencv::core;
pub mod pgm;
#[cfg(feature="ipp")]
pub mod ipp;
#[cfg(feature="opencvlib")]
pub mod cvutils;
pub mod index;
pub mod draw;
#[derive(Debug, Clone)]
pub struct Image<N>
where
N : Scalar
{
buf : Vec<N>,
ncols : usize
}
impl<N> Image<N>
where
N : Scalar + Copy
{
pub fn new_from_slice(source : &[N], ncols : usize) -> Self {
let mut buf = Vec::with_capacity(source.len());
unsafe { buf.set_len(source.len()); }
buf.copy_from_slice(&source);
Self{ buf, ncols }
}
pub fn from_vec(buf : Vec<N>, ncols : usize) -> Self {
if buf.len() as f64 % ncols as f64 != 0.0 {
panic!("Invalid image lenght");
}
Self { buf, ncols }
}
pub fn new_constant(nrows : usize, ncols : usize, value : N) -> Self {
let mut buf = Vec::with_capacity(nrows * ncols);
buf.extend((0..(nrows*ncols)).map(|_| value ));
Self{ buf, ncols }
}
pub fn shape(&self) -> (usize, usize) {
(self.buf.len() / self.ncols, self.ncols)
}
pub fn width(&self) -> usize {
self.ncols
}
pub fn height(&self) -> usize {
self.buf.len() / self.ncols
}
pub fn full_window<'a>(&'a self) -> Window<'a, N> {
self.window((0, 0), self.shape()).unwrap()
}
pub fn full_window_mut<'a>(&'a mut self) -> WindowMut<'a, N> {
let shape = self.shape();
self.window_mut((0, 0), shape).unwrap()
}
pub fn window<'a>(&'a self, offset : (usize, usize), sz : (usize, usize)) -> Option<Window<'a, N>> {
let orig_sz = self.shape();
if offset.0 + sz.0 <= orig_sz.0 && offset.1 + sz.1 <= orig_sz.1 {
Some(Window {
win : &self.buf[..],
offset,
orig_sz,
win_sz : sz
})
} else {
None
}
}
pub fn window_mut<'a>(&'a mut self, offset : (usize, usize), sz : (usize, usize)) -> Option<WindowMut<'a, N>> {
let orig_sz = self.shape();
if offset.0 + sz.0 <= orig_sz.0 && offset.1 + sz.1 <= orig_sz.1 {
Some(WindowMut {
win : &mut self.buf[..],
offset,
orig_sz,
win_sz : sz
})
} else {
None
}
}
pub fn downsample(&mut self, src : &Window<N>) {
assert!(src.is_full());
let src_ncols = src.orig_sz.1;
let dst_ncols = self.ncols;
#[cfg(feature="opencvlib")]
unsafe {
cvutils::resize(
src.win,
&mut self.buf[..],
src_ncols,
None,
dst_ncols,
None
);
return;
}
#[cfg(feature="ipp")]
unsafe {
let dst_nrows = self.buf.len() / self.ncols;
ipp::resize(src.win, &mut self.buf, src.orig_sz, (dst_nrows, dst_ncols));
}
panic!("Image::downsample requires that crate is compiled with opencv or ipp feature");
}
pub fn downsample_aliased<M>(&mut self, src : &Window<M>)
where
M : Scalar + Copy,
N : Scalar + From<M>
{
let (nrows, ncols) = self.shape();
let step_rows = src.win_sz.0 / nrows;
let step_cols = src.win_sz.1 / ncols;
assert!(step_rows == step_cols);
sampling::slices::subsample_convert_with_offset(
src.win,
src.offset,
src.win_sz,
(nrows, ncols),
step_rows,
self.buf.chunks_mut(nrows)
);
}
pub fn copy_from(&mut self, other : &Image<N>) {
self.buf.copy_from_slice(other.buf.as_slice());
}
pub fn as_slice(&self) -> &[N] {
&self.buf[..]
}
pub fn as_mut_slice(&mut self) -> &mut [N] {
&mut self.buf[..]
}
pub fn windows(&self, sz : (usize, usize)) -> impl Iterator<Item=Window<'_, N>>
where
N : Mul<Output=N> + MulAssign
{
self.full_window().windows(sz)
}
pub fn convert<M>(&mut self, other : &Window<M>)
where
M : Scalar
{
let ncols = self.ncols;
#[cfg(feature="opencvlib")]
unsafe {
cvutils::convert(
other.win,
&mut self.buf[..],
other.orig_sz.1,
Some((other.offset, other.win_sz)),
ncols,
None
);
return;
}
#[cfg(feature="ipp")]
{
assert!(other.is_full());
unsafe { ipp::convert(other.win, &mut self.buf[..], ncols); }
return;
}
panic!("Either opencvlib or ipp feature should be enabled for image conversion");
}
pub fn iter(&self) -> impl Iterator<Item=&N> {
let shape = self.shape();
iterate_row_wise(&self.buf[..], (0, 0), shape, shape)
}
pub fn len(&self) -> usize {
self.buf.len()
}
}
impl<N> Image<N>
where N : Scalar + Copy + RealField
{
pub fn scale_by(&mut self, scalar : N) {
self.buf.iter_mut().for_each(|val| *val *= scalar);
}
pub fn unscale_by(&mut self, scalar : N) {
self.buf.iter_mut().for_each(|val| *val *= scalar);
}
}
impl Image<u8> {
pub fn draw(&mut self, mark : Mark) {
self.full_window_mut().draw(mark);
}
}
impl Image<f32> {
pub fn max(&self) -> ((usize, usize), f32) {
let (mut max_ix, mut max) = ((0, 0), f32::NEG_INFINITY);
for (lin_ix, px) in self.iter().enumerate() {
if *px > max {
max_ix = index::coordinate_index(lin_ix, self.ncols);
max = *px
}
}
(max_ix, max)
}
}
impl<N> Index<(usize, usize)> for Image<N>
where
N : Scalar
{
type Output = N;
fn index(&self, index: (usize, usize)) -> &Self::Output {
&self.buf[index::linear_index(index, self.ncols)]
}
}
#[derive(Debug, Clone)]
pub struct Window<'a, N>
where
N : Scalar
{
offset : (usize, usize),
orig_sz : (usize, usize),
win_sz : (usize, usize),
win : &'a [N],
}
impl<'a, N> Window<'a, N>
where
N : Scalar
{
pub fn is_full(&'a self) -> bool {
self.orig_sz == self.win_sz
}
pub fn sub_window(&'a self, offset : (usize, usize), dims : (usize, usize)) -> Option<Window<'a, N>> {
let new_offset = (self.offset.0 + offset.0, self.offset.1 + offset.1);
if new_offset.0 + dims.0 <= self.orig_sz.0 && new_offset.1 + dims.1 <= self.orig_sz.1 {
Some(Self {
win : self.win,
offset : new_offset,
orig_sz : self.orig_sz,
win_sz : dims
})
} else {
None
}
}
}
impl<'a, N> Window<'a, N>
where
N : Scalar + Mul<Output=N> + MulAssign
{
pub fn from_square_slice(src : &'a [N]) -> Self {
Self::from_slice(src, (src.len() as f64).sqrt() as usize)
}
pub fn from_slice(src : &'a [N], ncols : usize) -> Self {
let nrows = src.len() / ncols;
Self{
win : src,
offset : (0, 0),
orig_sz : (nrows, ncols),
win_sz : (nrows, ncols),
}
}
pub fn shape(&self) -> (usize, usize) {
self.win_sz
}
pub fn width(&self) -> usize {
self.win_sz.1
}
pub fn height(&self) -> usize {
self.win_sz.0
}
pub fn len(&self) -> usize {
self.win_sz.0 * self.win_sz.1
}
pub fn windows(self, sz : (usize, usize)) -> impl Iterator<Item=Window<'a, N>> {
let (step_v, step_h) = sz;
if sz.0 >= self.win_sz.0 || sz.1 >= self.win_sz.1 {
panic!("Child window size bigger than parent window size");
}
if self.height() % sz.0 != 0 || self.width() % sz.1 != 0 {
panic!("Image size should be a multiple of window size (Required window {:?} over parent window {:?})", sz, self.win_sz);
}
let offset = self.offset;
WindowIterator::<'a, N> {
source : self,
size : sz,
curr_pos : offset,
step_v,
step_h
}
}
pub fn row(&self, ix : usize) -> Option<&[N]> {
if ix > self.win_sz.0 {
return None;
}
let stride = self.orig_sz.1;
let tl = self.offset.0 * stride + self.offset.1;
let start = tl + ix*stride;
Some(&self.win[start..(start+self.win_sz.1)])
}
pub fn rows(&self) -> impl Iterator<Item=&[N]> + Clone {
let stride = self.orig_sz.1;
let tl = self.offset.0 * stride + self.offset.1;
(0..self.win_sz.0).map(move |i| {
let start = tl + i*stride;
&self.win[start..(start+self.win_sz.1)]
})
}
pub fn iter(&self) -> impl Iterator<Item=&N> {
iterate_row_wise(self.win, self.offset, self.win_sz, self.orig_sz)
}
pub fn clone_owned(&self) -> Image<N>
where
N : Copy
{
let mut buf = Vec::new();
self.rows().for_each(|row| buf.extend(row.iter().cloned()) );
Image::from_vec(buf, self.win_sz.1)
}
}
#[test]
fn window_iter() {
let img : Window<'_, u8> = Window::from_square_slice(&[
1, 1, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1,
]);
for win in img.windows((4,4)) {
println!("Outer: {:?}", win);
for win_inner in win.windows((2,2)) {
println!("\tInner : {:?}", win_inner);
}
println!("")
}
}
impl<N> Index<(usize, usize)> for Window<'_, N>
where
N : Scalar
{
type Output = N;
fn index(&self, index: (usize, usize)) -> &Self::Output {
let off_ix = (self.offset.0 + index.0, self.offset.1 + index.1);
let (limit_row, limit_col) = (self.offset.0 + self.win_sz.0, self.offset.1 + self.win_sz.1);
if off_ix.0 < limit_row && off_ix.1 < limit_col {
&self.win[index::linear_index(off_ix, self.orig_sz.1)]
} else {
panic!("Invalid window index: {:?}", index);
}
}
}
pub fn iterate_row_wise<N>(
src : &[N],
offset : (usize, usize),
win_sz : (usize, usize),
orig_sz : (usize, usize)
) -> impl Iterator<Item=&N> {
let start = orig_sz.1 * offset.0 + offset.1;
(0..win_sz.0).map(move |i| {
let row_offset = start + i*orig_sz.1;
&src[row_offset..(row_offset+win_sz.1)]
}).flatten()
}
pub struct WindowIterator<'a, N>
where
N : Scalar,
{
source : Window<'a, N>,
size : (usize, usize),
curr_pos : (usize, usize),
step_v : usize,
step_h : usize,
}
impl<'a, N> Iterator for WindowIterator<'a, N>
where
N : Scalar
{
type Item = Window<'a, N>;
fn next(&mut self) -> Option<Self::Item> {
let within_horiz = self.curr_pos.0 + self.size.0 <= (self.source.offset.0 + self.source.win_sz.0);
let within_vert = self.curr_pos.1 + self.size.1 <= (self.source.offset.1 + self.source.win_sz.1);
let within_bounds = within_horiz && within_vert;
let win = if within_bounds {
Some(Window {
offset : self.curr_pos,
win_sz : self.size,
orig_sz : self.source.orig_sz,
win : &self.source.win
})
} else {
None
};
self.curr_pos.1 += self.step_h;
if self.curr_pos.1 + self.size.1 > (self.source.offset.1 + self.source.win_sz.1) {
self.curr_pos.1 = self.source.offset.1;
self.curr_pos.0 += self.step_v;
}
win
}
}
#[cfg(feature="opencvlib")]
impl<N> Into<core::Mat> for Window<'_, N>
where
N : Scalar + Copy
{
fn into(self) -> core::Mat {
let sub_slice = Some((self.offset, self.win_sz));
let stride = self.orig_sz.1;
unsafe{ cvutils::slice_to_mat(self.win, stride, sub_slice) }
}
}
#[cfg(feature="opencvlib")]
impl<N> Into<core::Mat> for WindowMut<'_, N>
where
N : Scalar + Copy
{
fn into(self) -> core::Mat {
let sub_slice = Some((self.offset, self.win_sz));
let stride = self.orig_sz.1;
unsafe{ cvutils::slice_to_mat(self.win, stride, sub_slice) }
}
}
pub enum Mark {
Cross((usize, usize), usize, u8),
Corner((usize, usize), usize, u8),
Line((usize, usize), (usize, usize), u8),
Digit((usize, usize), usize, usize, u8),
Label((usize, usize), &'static str, usize, u8),
Circle((usize, usize), usize, u8)
}
#[derive(Debug)]
pub struct WindowMut<'a, N>
where
N : Scalar + Copy
{
offset : (usize, usize),
orig_sz : (usize, usize),
win_sz : (usize, usize),
win : &'a mut [N],
}
impl<'a, N> WindowMut<'a, N>
where
N : Scalar + Copy + Debug
{
pub fn from_slice(src : &'a mut [N], ncols : usize) -> Self {
let nrows = src.len() / ncols;
Self{
win : src,
offset : (0, 0),
orig_sz : (nrows, ncols),
win_sz : (nrows, ncols),
}
}
pub fn sub_window_mut(&'a mut self, offset : (usize, usize), dims : (usize, usize)) -> Option<WindowMut<'a, N>> {
let new_offset = (self.offset.0 + offset.0, self.offset.1 + offset.1);
if new_offset.0 + dims.0 <= self.orig_sz.0 && new_offset.1 + dims.1 <= self.orig_sz.1 {
Some(Self {
win : self.win,
offset : (self.offset.0 + offset.0, self.offset.1 + offset.1),
orig_sz : self.orig_sz,
win_sz : dims
})
} else {
None
}
}
pub fn from_square_slice(src : &'a mut [N]) -> Self {
Self::from_slice(src, (src.len() as f64).sqrt() as usize)
}
}
impl WindowMut<'_, u8> {
pub fn draw(&mut self, mark : Mark) {
match mark {
Mark::Cross(pos, sz, col) => {
let cross_pos = (self.offset.0 + pos.0, self.offset.1 + pos.1);
draw::draw_cross(
self.win,
self.orig_sz,
cross_pos,
col,
sz
);
},
Mark::Corner(pos, sz, col) => {
let center_pos = (self.offset.0 + pos.0, self.offset.1 + pos.1);
draw::draw_corners(
self.win,
self.orig_sz,
center_pos,
col,
sz
);
},
Mark::Line(src, dst, color) => {
let src_pos = (self.offset.0 + src.0, self.offset.1 + src.1);
let dst_pos = (self.offset.0 + dst.0, self.offset.1 + dst.1);
#[cfg(feature="opencvlib")]
unsafe {
cvutils::draw_line(self.win, self.orig_sz.1, src_pos, dst_pos, color);
return;
}
draw::draw_line(
self.win,
self.orig_sz,
src_pos,
dst_pos,
color
);
},
Mark::Digit(pos, val, sz, color) => {
let tl_pos = (self.offset.0 + pos.0, self.offset.1 + pos.1);
#[cfg(feature="opencvlib")]
unsafe {
cvutils::write_text(self.win, self.orig_sz.1, tl_pos, &val.to_string()[..], color);
return;
}
draw::draw_digit_native(self.win, self.orig_sz.1, tl_pos, val, sz, color);
},
Mark::Label(pos, msg, sz, color) => {
let tl_pos = (self.offset.0 + pos.0, self.offset.1 + pos.1);
#[cfg(feature="opencvlib")]
unsafe {
cvutils::write_text(self.win, self.orig_sz.1, tl_pos, msg, color);
return;
}
panic!("Label draw require 'opencvlib' feature");
},
Mark::Circle(pos, radius, color) => {
let center_pos = (self.offset.0 + pos.0, self.offset.1 + pos.1);
#[cfg(feature="opencvlib")]
unsafe {
cvutils::draw_circle(self.win, self.orig_sz.1, center_pos, radius, color);
return;
}
panic!("Circle draw require 'opencvlib' feature");
}
}
}
}
impl<'a, N> WindowMut<'a, N>
where
N : Scalar + Copy
{
pub fn shape(&self) -> (usize, usize) {
self.win_sz
}
pub fn width(&self) -> usize {
self.shape().0
}
pub fn height(&self) -> usize {
self.shape().1
}
}
impl<'a, N> WindowMut<'a, N>
where
N : Scalar + Copy + MulAssign + AddAssign + Add<Output=N> + Mul<Output=N> + SubAssign + Field + SimdPartialOrd,
f64 : SubsetOf<N>
{
pub fn component_scale(&mut self, _other : &Window<N>) {
unimplemented!()
}
}
impl<N> Index<(usize, usize)> for WindowMut<'_, N>
where
N : Scalar + Copy
{
type Output = N;
fn index(&self, _index: (usize, usize)) -> &Self::Output {
unimplemented!()
}
}
impl<N> IndexMut<(usize, usize)> for WindowMut<'_, N>
where
N : Scalar + Copy
{
fn index_mut(&mut self, _index: (usize, usize)) -> &mut Self::Output {
unimplemented!()
}
}
impl<'a, N> AsRef<DMatrixSlice<'a, N>> for Window<'a, N>
where
N : Scalar + Copy
{
fn as_ref(&self) -> &DMatrixSlice<'a, N> {
unimplemented!()
}
}
impl<N> AsRef<[N]> for Image<N>
where
N : Scalar
{
fn as_ref(&self) -> &[N] {
&self.buf[..]
}
}
impl<N> AsMut<[N]> for Image<N>
where
N : Scalar
{
fn as_mut(&mut self) -> &mut [N] {
&mut self.buf[..]
}
}
impl<N> fmt::Display for Window<'_, N>
where
N : Scalar + Copy,
f64 : From<N>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", pgm::build_pgm_string_from_slice(&self.win, self.orig_sz.1))
}
}
impl<N> fmt::Display for WindowMut<'_, N>
where
N : Scalar + Copy,
f64 : From<N>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", pgm::build_pgm_string_from_slice(&self.win, self.orig_sz.1))
}
}
impl<N> fmt::Display for Image<N>
where
N : Scalar + Copy,
f64 : From<N>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", pgm::build_pgm_string_from_slice(&self.buf[..], self.ncols))
}
}
#[test]
fn checkerboard() {
let src : [u8; 4] = [0, 1, 1, 0];
let mut converted : Image<f32> = Image::new_constant(4, 4, 0.0);
let win = Window::from_square_slice(&src);
}