/************************************************************************/
/*                                                                      */
/*    vspline - a set of generic tools for creation and evaluation      */
/*              of uniform b-splines                                    */
/*                                                                      */
/*            Copyright 2015 - 2023 by Kay F. Jahnke                    */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

/// \file gsm.cc
///
/// \brief  calculating the gradient squared magnitude, derivatives
///
/// implementation of gsm.cc, performing the calculation of the
/// gradient squared magnitude in a loop using two evaluators for
/// the two derivatives, adding the squared magnitudes and writing
/// the result to an image file
///
/// compile with:
/// clang++ -std=c++11 -march=native -o gsm -O3 -pthread -DUSE_VC gsm.cc -lvigraimpex -lVc
/// or: clang++ -std=c++11 -march=native -o gsm -O3 -pthread gsm.cc -lvigraimpex
/// (with Vc; use -DUSE_HWY and -lhwy for highway, or -std=c++17 and -DUSE_STDSIMD
/// for the std::simd backend)
///
/// invoke passing a colour image file. the result will be written to 'gsm.tif'

#include <iostream>

#include <vspline/vspline.h>

#include <vigra/stdimage.hxx>
#include <vigra/imageinfo.hxx>
#include <vigra/impex.hxx>

// we silently assume we have a colour image
typedef vigra::RGBValue<float,0,1,2> pixel_type; 

// coordinate_type iss a 2D single precision coordinate
typedef vigra::TinyVector < float , 2 > coordinate_type ;

// target_type is a 2D array of pixels  
typedef vigra::MultiArray < 2 , pixel_type > target_type ;

// b-spline evaluator producing float pixels
typedef vspline::evaluator < coordinate_type ,
                             pixel_type
                           > ev_type ;

int main ( int argc , char * argv[] )
{
  if ( argc < 2 )
  {
    std::cerr << "pass a colour image file as argument" << std::endl ;
    exit( -1 ) ;
  }
  
  vigra::ImageImportInfo imageInfo ( argv[1] ) ;

  // we want a b-spline with natural boundary conditions
  
  vigra::TinyVector < vspline::bc_code , 2 > bcv ( vspline::NATURAL ) ;
  
  // create cubic 2D b-spline object to receive the image data
  
  vspline::bspline < pixel_type , 2 > bspl ( imageInfo.shape() , 3 , bcv ) ;
  
  // load the image data into the bspline object's 'core'
  
  vigra::importImage ( imageInfo , bspl.core ) ;
  
  // prefilter the b-spline
  
  bspl.prefilter() ;
  
  // we create two evaluators for the b-spline, one for the horizontal and
  // one for the vertical gradient. The derivatives for a b-spline are requested
  // by passing a TinyVector with as many elements as the spline's dimension
  // with the desired derivative degree for each dimension. Here we want the
  // first derivative in x and y direction:
  
  const vigra::TinyVector < float , 2 > dx1_spec { 1 , 0 } ;
  const vigra::TinyVector < float , 2 > dy1_spec { 0 , 1 } ;
  
  // we pass the derivative specifications to the two evaluators' constructors
  
  ev_type xev ( bspl , dx1_spec ) ;
  ev_type yev ( bspl , dy1_spec ) ;
  
  // this is where the result should go:

  target_type target ( imageInfo.shape() ) ;

  // quick-shot solution, iterating in a loop, not vectorized
  
  for ( int y = 0 ; y < target.shape(1) ; y++ )
  {
    for ( int x = 0 ; x < target.shape(0) ; x++ )
    {
      coordinate_type crd { float ( x ) , float ( y ) } ;
      
      // now we get the two gradients by evaluating the gradient evaluators
      // at the given coordinate
      
      pixel_type dx , dy ;
      
      xev.eval ( crd , dx ) ;
      yev.eval ( crd , dy ) ;
      
      // and conclude by writing the sum of the squared gradients to target

      target [ crd ] = dx * dx + dy * dy ;
    }
  }
  
  // store the result with vigra impex
  
  vigra::ImageExportInfo eximageInfo ( "gsm.tif" );
  
  std::cout << "storing the target image as 'gsm.tif'" << std::endl ;
  
  vigra::exportImage ( target ,
                       eximageInfo
                       .setPixelType("UINT8") ) ;
  
  exit ( 0 ) ;
}
