アフィン変換サンプル

枠内に画像をドラッグ&ドロップしてください

出力

コードが原型を留めなくなるのでシンプルなコードを置いておく
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;
                           },
                       }
                   }    
               }
           }
       }   
   }