/*
 * ROTATE_SCALE: rotate and scale a matrix using bilinear interpolation
 * imo= block(im, xregs, yregs);
 *
 * Copyright (C) 2003 Andy Adler
 * This code has no warrany whatsoever.
 * Do what you like with this code as long as you
 *     leave this copyright in place.
 *
 * $Id: rotate_scale.cc,v 1.2 2005/04/22 17:29:42 aadler Exp $
 */

#include <octave/oct.h>

void
calc_rotation_params(
              double x0l,double y0l,double x0r,double y0r,
              double x1l,double y1l,double x1r,double y1r,
              double* Tx_x, double* Ty_x,
              double* Tx_y, double* Ty_y,
              double* Tx_1, double* Ty_1 );
void
do_interpolation (
              double Tx_x, double Ty_x,
              double Tx_y, double Ty_y,
              double Tx_1, double Ty_1,
              int x0max, int y0max,// initial size
              int x1max, int y1max,// output size
              const double * img0,
              double       * img1 );

DEFUN_DLD (rotate_scale, args, ,
  "ROTATE_SCALE: arbitrary rotation and scaling of an image\n"
  "              using fast bilinear interpolation\n"
  "im1 = rotate_scale(im0, lm0, lm1, out_size)\n"
  "  where:\n"
  "im0 = input image\n"
  "lm0 = landmarks of points in original image [ x1,x2;y1,y2 ]\n"
  "im1 = output image, where size(im1) == out_size\n"
  "lm1 = landmarks of points in output image [ x1,x2;y1,y2 ]\n"
  "\n"
  "   note1: two landmarks must be specified for lm0 and lm1\n"
  "   note2: all images have a single component\n"
  "   to use this for colour images, use:\n"
  "  r_im1= rotate_scale( red_im0, lm0, lm1, out_size)\n"
  "  g_im1= rotate_scale( grn_im0, lm0, lm1, out_size)\n"
  "  b_im1= rotate_scale( blu_im0, lm0, lm1, out_size)\n"
  "\n"
  "   example:\n"
  "  im0= zeros(100); im0(25:75,25:75)=1;\n"
  "  im1= rotate_scale( im0, [40,60;50,50],[60,90;60,90],[120,120]);\n"
)
{
   octave_value_list retval;
   if (args.length() < 4 ||
       !args(0).is_matrix_type() ||
       !args(1).is_matrix_type() ||
       !args(2).is_matrix_type() ||
       !args(3).is_matrix_type()
       ) {
      print_usage ("rotate_scale");
      return retval;
   }

   Matrix im0( args(0).matrix_value() );
   const double * im0p = im0.data();
   Matrix lm0( args(1).matrix_value() );
   Matrix lm1( args(2).matrix_value() );
   ColumnVector out_size( args(3).vector_value() );

   int inp_hig= im0.rows();
   int inp_wid= im0.cols();

   int out_hig= (int) out_size(0);
   int out_wid= (int) out_size(1);
   Matrix im1( out_hig, out_wid);
   double * im1p = im1.fortran_vec();

   double Tx_x; double Ty_x;
   double Tx_y; double Ty_y;
   double Tx_1; double Ty_1;
   calc_rotation_params(
          lm0(0,0), lm0(1,0), lm0(0,1), lm0(1,1),
          lm1(0,0), lm1(1,0), lm1(0,1), lm1(1,1),
          & Tx_x, & Ty_x,
          & Tx_y, & Ty_y,
          & Tx_1, & Ty_1 );
    
   do_interpolation( Tx_x, Ty_x, Tx_y, Ty_y, Tx_1, Ty_1,
                  inp_wid, inp_hig, out_wid, out_hig,
                  im0p, im1p );

   retval(0) = im1;
   return retval;
}

inline double sqr(double a) { return (a)*(a); }

void
calc_rotation_params(
              double x1l,double y1l,double x1r,double y1r,
              double x0l,double y0l,double x0r,double y0r,
              double* Tx_x, double* Ty_x,
              double* Tx_y, double* Ty_y,
              double* Tx_1, double* Ty_1 
              )
{
    double d0= sqrt( sqr(x0l-x0r) + sqr(y0l-y0r) );
    double d1= sqrt( sqr(x1l-x1r) + sqr(y1l-y1r) );
    double dr= d1/d0;

    double a0= atan2( y0l-y0r , x0l-x0r );
    double a1= atan2( y1l-y1r , x1l-x1r );
    double ad= a1-a0;
    double dr_cos_ad= dr*cos(ad);
    double dr_sin_ad= dr*sin(ad);

    double x0m= (x0l+x0r)/2;
    double y0m= (y0l+y0r)/2;
    double x1m= (x1l+x1r)/2;
    double y1m= (y1l+y1r)/2;

    *Tx_x=  dr_cos_ad;
    *Ty_x=  dr_sin_ad;
    *Tx_y= -dr_sin_ad;
    *Ty_y=  dr_cos_ad;
    *Tx_1=  x1m - dr_cos_ad*x0m + dr_sin_ad*y0m;
    *Ty_1=  y1m - dr_sin_ad*x0m - dr_cos_ad*y0m;
}    


void
do_interpolation (
              double Tx_x, double Ty_x,
              double Tx_y, double Ty_y,
              double Tx_1, double Ty_1,
              int x0max, int y0max,// initial size
              int x1max, int y1max,// output size
              const double * img0,
              double       * img1 
            )
{

    for (int i=0; i< x1max; i++) {
        for (int j=0; j< y1max; j++) {
            double x0i= Tx_x * i + Tx_y * j + Tx_1;
            double y0i= Ty_x * i + Ty_y * j + Ty_1;

            if ( x0i < 0       )    x0i= 0;
            else
            if (x0i >= x0max-1 )    x0i= x0max - 1.00001; 

            if ( y0i < 0       )    y0i= 0;
            else
            if (y0i >= y0max-1 )    y0i= y0max - 1.00001; 

            int x0idx= (int) x0i;
            int y0idx= (int) y0i;

            double frac_r= x0i- x0idx; 
            double frac_l= 1 - frac_r;
            double frac_d= y0i- y0idx; 
            double frac_u= 1 - frac_d;

            int pix_lu= (y0idx+0) + (x0idx+0) * y0max ;
            int pix_ru= (y0idx+0) + (x0idx+1) * y0max ;
            int pix_ld= (y0idx+1) + (x0idx+0) * y0max ;
            int pix_rd= (y0idx+1) + (x0idx+1) * y0max ;
               
            img1[ i*y1max + j ]=
               frac_l*frac_u* img0[ pix_lu ] +
               frac_r*frac_u* img0[ pix_ru ] +
               frac_l*frac_d* img0[ pix_ld ] +
               frac_r*frac_d* img0[ pix_rd ];

        }
    }
}



syntax highlighted by Code2HTML, v. 0.9.1