枠内に画像をドラッグ&ドロップしてください
出力
コードが原型を留めなくなるのでシンプルなコードを置いておく
2022/04/10 逆アフィン変換が一部間違ってたのでfix
ソースコードを表示
/* * affine.rs Mith@mmk (C) 2022 * create 2022/03/13 */ use core::cmp::max; use core::cmp::min; use std::cmp::Ordering; use core::f32::consts::PI; use super::canvas::*; pub enum InterpolationAlgorithm { NearestNeighber, Bilinear, Bicubic, BicubicAlpha(Option<f32>), Lanzcos3, Lanzcos(Option<usize>), } pub struct Affine { affine: [[f32;3];3], // 3*3 } impl Affine { pub fn new() -> Self { let affine = [ [1.0,0.0,0.0], [0.0,1.0,0.0], [0.0,0.0,1.0]]; Self { affine, } } fn matrix(self:&mut Self,f: &[[f32;3];3]) { let affin = self.affine; let mut result:[[f32;3];3] = [[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]; for i in 0..3 { for j in 0..3 { result[i][j] = affin[i][0] * f[0][j] + affin[i][1] * f[1][j] + affin[i][2] * f[2][j]; } } self.affine = result; } pub fn translation(self:&mut Self,x:f32,y:f32) { self.matrix(&[[1.0 ,0.0 , x], [0.0 ,1.0 , y], [0.0 ,0.0 ,1.0]]); } pub fn invert_x(self:&mut Self) { self.matrix(&[[-1.0 , 0.0 , 0.0], [ 0.0 , 1.0 , 0.0], [ 0.0 , 0.0 , 1.0]]); } pub fn invert_y(self:&mut Self) { self.matrix(&[[ 1.0 , 0.0 , 0.0], [ 0.0 , -1.0 , 0.0], [ 0.0 , 0.0 , 1.0]]); } pub fn invert_xy(self:&mut Self) { self.matrix(&[[-1.0 , 0.0 , 0.0], [ 0.0 ,-1.0 , 0.0], [ 0.0 , 0.0 , 1.0]]); } pub fn scale(self:&mut Self,x:f32,y:f32) { self.matrix(&[[ x ,0.0 , 0.0], [ 0.0 , y , 0.0], [ 0.0 ,0.0 , 1.0]]); } pub fn rotate_by_dgree(self:&mut Self,theta:f32) { let theta = PI * theta / 180.0; self.rotate(theta); } pub fn rotate(self:&mut Self,theta:f32) { let c = theta.cos(); let s = theta.sin(); self.matrix(&[[ c , -s , 0.0], [ s , c , 0.0], [ 0.0 ,0.0 , 1.0]]); } pub fn shear(self:&mut Self,x:f32,y:f32) { self.matrix(&[[ 1.0 , y , 0.0], [ x ,1.0 , 0.0], [ 0.0 ,0.0 , 1.0]]); } #[inline] fn sinc (x:f32) -> f32 { (x * PI).sin() / (x * PI) } /* not implement Scaling Routine */ pub fn _conversion(self:&mut Self,input_canvas: &Canvas,output_canvas:&mut Canvas) { let min_x = 0; let max_x = output_canvas.width() as i32; let min_y = 0; let max_y = output_canvas.height() as i32; let ox = max_x / 2; let oy = max_y / 2; /* * |X| |a00 a01 a02||x| X = a00 * x + a01 * y + a02 * |Y| = |a10 a11 a12||y| Y = a10 * x + a11 * y + a12 * |Z| |a20 a21 a22||1| _ do not use */ let x0 = self.affine[0][0]; let x1 = self.affine[0][1]; let x2 = self.affine[0][2]; let y0 = self.affine[1][0]; let y1 = self.affine[1][1]; let y2 = self.affine[1][2]; for y in 0..input_canvas.height() as usize { let input_base_line = input_canvas.width() as usize * 4 * y; for x in 0..input_canvas.width() as usize { let offset = input_base_line + x * 4; let x_ = x as i32 - ox; let y_ = y as i32 - oy; let xx_ = x_ as f32 * x0 + y_ as f32 * x1 + x2; let xx = (xx_ + ox as f32).round() as i32; if xx < min_x || xx >= max_x {continue} // Out of bound let yy_ = x_ as f32 * y0 + y_ as f32 * y1 + y2; let yy = (yy_ + oy as f32).round() as i32; if yy < min_y || yy >= max_y {continue} // Out of bound let output_offset = ((yy * max_x + xx) *4) as usize; output_canvas.buffer[output_offset ] = input_canvas.buffer[offset ]; output_canvas.buffer[output_offset + 1] = input_canvas.buffer[offset + 1]; output_canvas.buffer[output_offset + 2] = input_canvas.buffer[offset + 2]; output_canvas.buffer[output_offset + 3] = input_canvas.buffer[offset + 3]; } } } pub fn conversion(self:&mut Self,input_canvas: &Canvas,output_canvas:&mut Canvas,algorithm:InterpolationAlgorithm) { let start_x = 0 as f32; let start_y = 0 as f32; let out_start_x = 0; let out_start_y = 0; let out_width = output_canvas.width() as i32; let out_height = output_canvas.height() as i32; self.conversion_with_area(input_canvas,output_canvas, start_x as f32,start_y,input_canvas.width() as f32,input_canvas.height() as f32, out_start_x,out_start_y,out_width as i32,out_height as i32, algorithm); } pub fn conversion_with_area(self:&mut Self,input_canvas: &Canvas,output_canvas:&mut Canvas, start_x :f32,start_y:f32 ,width: f32,height: f32, out_start_x :i32,out_start_y:i32 ,out_width: i32,out_height: i32, algorithm:InterpolationAlgorithm) { let end_x = width - start_x - 1.0; let end_y = height - start_y - 1.0; let out_end_x = out_width - out_start_x - 1; let out_end_y = out_height - out_start_y - 1; let ox = (out_width / 2) as f32; let oy = (out_height / 2) as f32; let mut alpha = -0.5; // -0.5 - -1.0 let mut lanzcos_n = 3; match algorithm { InterpolationAlgorithm::BicubicAlpha(a) =>{ if a.is_some() { alpha = a.unwrap() } }, InterpolationAlgorithm::Lanzcos(n) => { if n.is_some() { lanzcos_n = n.unwrap(); } } _ => {} } /* * |X| |a00 a01 a02||x| X = a00 * x + a01 * y + a02 * |Y| = |a10 a11 a12||y| Y = a10 * x + a11 * y + a12 * |Z| |a20 a21 a22||1| _ do not use */ let x0 = self.affine[0][0]; let x1 = self.affine[0][1]; let x2 = self.affine[0][2]; let y0 = self.affine[1][0]; let y1 = self.affine[1][1]; let y2 = self.affine[1][2]; // calc rectangle 4x affine trans let mut xy = [(0_i32,0_i32);4]; let x = start_x - ox; let y = start_y - oy; xy[0] = ((x * x0 + y * x1 + x2 + ox) as i32 ,(x * y0 + y * y1 + y2 +oy) as i32); let x = end_x - ox; let y = start_y - oy ; xy[1] = ((x * x0 + y * x1 + x2 + ox) as i32 ,(x * y0 + y * y1 + y2 +oy) as i32); let x = start_x - ox; let y = end_y - oy; xy[2] = ((x * x0 + y * x1 + x2 + ox) as i32 ,(x * y0 + y * y1 + y2 +oy) as i32); let x = end_x - ox; let y = end_y - oy; xy[3] = ((x * x0 + y * x1 + x2 + ox) as i32 ,(x * y0 + y * y1 + y2 +oy) as i32); xy.sort_by(|a, b| if a.1 == b.1 { if a.0 < b.0 { Ordering::Less } else if a.0 > b.0 { Ordering::Greater } else { Ordering::Equal } } else if a.1 < b.1 { Ordering::Less } else { Ordering::Greater }); // pre-calc inverse affine transformation // x = y1 * X - x0 * Y + x1 * y2 - y1 * x2 // y = -y0 * X + x1 * X + y0 * x2 - x0 * y2 let t = x0 * y1 - x1 * y0; let ix0 = y1; let ix1 = -x1; let ix2 = x1 * y2 - y1 * x2; let iy0 = -y0; let iy1 = x0; let iy2 = y0 * x2 - x0 * y2; // stage 0 y0..y1 (x0,y0) - (x1,y1) & (x0,y0) - (x2,y2) // stage 1 y1..y2 (x0,y0) - (x2,x2) & (x1,y1) - (x3,y3) // stage 2 y2..y3 (x1,y1) - (x3,y3) & (x2,y2) - (x3,y3) for stage in 0..3 { let mut sy; let mut ey; let xy0; let xy1; let xy2; let xy3; match stage { 0 => { sy = xy[0].1; ey = xy[1].1; xy0 = xy[0]; xy1 = xy[1]; xy2 = xy[0]; xy3 = xy[2]; }, 1 => { sy = xy[1].1; ey = xy[2].1; xy0 = xy[1]; xy1 = xy[3]; xy2 = xy[0]; xy3 = xy[2]; }, 2 => { sy = xy[2].1; ey = xy[3].1; xy0 = xy[1]; xy1 = xy[3]; xy2 = xy[2]; xy3 = xy[3]; }, _ => { sy = 0; ey = 0; xy0 = (0,0); xy1 = (0,0); xy2 = (0,0); xy3 = (0,0); } } if sy < out_start_y { sy = out_start_y;} if ey > out_end_y {ey = out_end_y;} let d0 = if xy0.1 != xy1.1 {(xy0.0 as f32 - xy1.0 as f32) / (xy0.1 as f32 - xy1.1 as f32)} else {0.0}; let d1 = if xy2.1 != xy3.1 {(xy2.0 as f32 - xy3.0 as f32) / (xy2.1 as f32 - xy3.1 as f32)} else {0.0}; for y in sy..ey { // (x0,y0) - (x1,y1) & (x2,y2) - (x3,y3) let (mut sx,mut ex) = if xy0.1 == xy1.1 { (min(xy0.0 ,xy1.0),max(xy0.0,xy1.0)+1) } else { if xy2.1 == xy3.1 { (min(xy2.0 ,xy3.0),max(xy2.0,xy3.0)+1) } else { let x0 = (d0 * (y - xy0.1) as f32) as i32 + xy0.0 as i32; let x1 = (d1 * (y - xy2.1) as f32) as i32 + xy2.0 as i32; (min(x0,x1),max(x0,x1)+1) } }; let output_base_line = output_canvas.width() as usize * 4 * y as usize; if sx < out_start_x { sx = out_start_x;} if ex > out_end_x {ex = out_end_x;} for x in sx..ex { // inverse affine transformation from output image integer position let xx = (ix0 * (x as f32 - ox) + ix1 * ( y as f32 - oy) + ix2 ) / t + ox; let yy = (iy0 * (x as f32 - ox) + iy1 * ( y as f32 - oy) + iy2 ) / t + oy; if xx < start_x || xx >= end_x || yy < start_y || yy >= end_x {continue;} let output_offset = output_base_line + x as usize * 4; let input_offset = (yy as usize * input_canvas.width() as usize + xx as usize) * 4; match algorithm { InterpolationAlgorithm::NearestNeighber => { output_canvas.buffer[output_offset ] = input_canvas.buffer[input_offset ]; output_canvas.buffer[output_offset + 1] = input_canvas.buffer[input_offset + 1]; output_canvas.buffer[output_offset + 2] = input_canvas.buffer[input_offset + 2]; output_canvas.buffer[output_offset + 3] = input_canvas.buffer[input_offset + 3]; }, InterpolationAlgorithm::Bilinear => { let dx = xx - xx.floor(); let dy = yy - yy.floor(); let xx = xx.floor() as i32; let yy = yy.floor() as i32; let nx = if xx + 1 > end_x as i32 {0} else {4}; let ny = if yy + 1 > end_y as i32 {0} else {input_canvas.width() as usize * 4}; let r =(input_canvas.buffer[input_offset ] as f32 * (1.0-dx) * (1.0-dy) + input_canvas.buffer[input_offset + nx] as f32 * dx * (1.0-dy) + input_canvas.buffer[input_offset + ny] as f32 * (1.0-dx) * dy + input_canvas.buffer[input_offset + nx + ny] as f32 * dx * dy) as i32; let g =(input_canvas.buffer[input_offset + 1 ] as f32 * (1.0-dx) * (1.0-dy) + input_canvas.buffer[input_offset + 1 + nx] as f32 * dx * (1.0-dy) + input_canvas.buffer[input_offset + 1 + ny] as f32 * (1.0-dx) * dy + input_canvas.buffer[input_offset + 1 + nx + ny] as f32 * dx * dy) as i32; let b =(input_canvas.buffer[input_offset + 2 ] as f32 * (1.0-dx) * (1.0-dy) + input_canvas.buffer[input_offset + 2 + nx] as f32 * dx * (1.0-dy) + input_canvas.buffer[input_offset + 2 + ny] as f32 * (1.0-dx) * dy + input_canvas.buffer[input_offset + 2 + nx + ny] as f32 * dx * dy) as i32; let a =(input_canvas.buffer[input_offset + 3 ] as f32 * (1.0-dx) * (1.0-dy) + input_canvas.buffer[input_offset + 3 + nx] as f32 * dx * (1.0-dy) + input_canvas.buffer[input_offset + 3 + ny] as f32 * (1.0-dx) * dy + input_canvas.buffer[input_offset + 3 + nx + ny] as f32 * dx * dy) as i32; output_canvas.buffer[output_offset ] = r.clamp(0,255) as u8; output_canvas.buffer[output_offset + 1] = g.clamp(0,255) as u8; output_canvas.buffer[output_offset + 2] = b.clamp(0,255) as u8; output_canvas.buffer[output_offset + 3] = a.clamp(0,255) as u8; }, InterpolationAlgorithm::Bicubic | InterpolationAlgorithm::BicubicAlpha(_) => { let dx = xx - xx.floor(); let dy = yy - yy.floor(); let xx = xx.floor() as i32; let yy = yy.floor() as i32; let mut color = [0.0;4]; for _y in 0..4 { let dy = _y as f32 - dy - 1.0 ; let dy = dy.abs(); let wy = if dy <= 1.0 { (alpha + 2.0) * dy.powi(3) - (alpha + 3.0) * dy.powi(2) + 1.0 } else if dy < 2.0 { alpha * dy.powi(3) - 5.0 * alpha * dy.powi(2) + 8.0 * alpha * dy - 4.0 * alpha } else {0.0}; let jy = _y - 1; let baseoffset = if yy + jy < start_y as i32 {start_y as isize * input_canvas.width() as isize * 4 } else if yy + jy >= end_y as i32 { end_y as isize * input_canvas.width() as isize * 4} else { ((yy + jy) as isize * input_canvas.width() as isize) * 4 }; for _x in 0..4 { let dx = _x as f32 - dx - 1.0; let dx = dx.abs(); let jx = _x - 1; let offset = if xx + jx <= start_x as i32 { baseoffset + start_x as isize * 4 } else if xx + jx >= end_x as i32 { baseoffset + end_x as isize * 4} else { baseoffset + (xx + jx) as isize * 4 }; let wx = if dx <= 1.0 { (alpha + 2.0) * dx.powi(3) - (alpha + 3.0) * dx.powi(2) + 1.0 } else if dx < 2.0 { alpha * dx.powi(3) - 5.0 * alpha * dx.powi(2) + 8.0 * alpha * dx - 4.0 * alpha } else {0.0}; let w = wx * wy; for i in 0..3 { color[i] += w * input_canvas.buffer[offset as usize + i] as f32; } } } output_canvas.buffer[output_offset ] = (color[0] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 1] = (color[1] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 2] = (color[2] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 3] = 0xff; }, InterpolationAlgorithm::Lanzcos3 | InterpolationAlgorithm::Lanzcos(_) => { let dx = xx - xx.floor(); let dy = yy - yy.floor(); let xx = xx.floor() as i32; let yy = yy.floor() as i32; let n = lanzcos_n as i32; let mut color = [0.0;4]; for _y in 0..2 * n { let jy = _y - n + 1; let dy = (jy as f32 - dy).abs(); let wy = if dy == 0.0 { 1.0 } else if dy < n as f32 { Self::sinc(dy) * Self::sinc(dy / n as f32) } else { 0.0 }; let baseoffset = if yy + jy < start_y as i32 {start_y as isize * input_canvas.width() as isize * 4 } else if yy + jy > end_y as i32 { end_y as isize * input_canvas.width() as isize * 4} else { ((yy + jy) as isize * input_canvas.width() as isize) * 4 }; for _x in 0..2 * n { let jx = _x - n + 1; let dx = (jx as f32 - dx).abs(); let wx = if dx == 0.0 { 1.0 } else if dx < n as f32 { Self::sinc(dx) * Self::sinc(dx / n as f32) } else {0.0}; let offset = if xx + jx <= start_x as i32 { baseoffset + start_x as isize * 4 } else if xx + jx >= end_x as i32 { baseoffset + end_x as isize * 4} else { baseoffset + (xx + jx) as isize * 4 }; let w = wx * wy; for i in 0..3 { color[i] += w * input_canvas.buffer[offset as usize + i] as f32; } } } output_canvas.buffer[output_offset ] = (color[0] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 1] = (color[1] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 2] = (color[2] as i32).clamp(0,255) as u8; output_canvas.buffer[output_offset + 3] = 0xff; }, } } } } } }