docs: def affine(data, mat=np.identity(4), mode="constant", interpolation="linear"):
      """
      affine transform data with matrix mat, which is the inverse coordinate transform matrix  
      (similar to ndimage.affine_transform)
       
      Parameters
      ----------
      data, ndarray
          3d array to be transformed
      mat, ndarray 
          3x3 or 4x4 inverse coordinate transform matrix 
      mode: string 
          boundary mode, one of the following:
          'constant'
              pads with zeros 
          'edge'
              pads with edge values
          'wrap'
              pads with the repeated version of the input 
      interpolation, string
          interpolation mode, one of the following    
          'linear'
          'nearest'
          
      Returns
      -------
      res: ndarray
          transformed array (same shape as input)
          
      """
      warnings.warn(
          "gputools.transform.affine: API change as of gputools>= 0.2.8: the inverse of the matrix is now used as in scipy.ndimage.affine_transform")
  
      if not (isinstance(data, np.ndarray) and data.ndim == 3):
          raise ValueError("input data has to be a 3d array!")
  
      interpolation_defines = {"linear": ["-D", "SAMPLER_FILTER=CLK_FILTER_LINEAR"],
                               "nearest": ["-D", "SAMPLER_FILTER=CLK_FILTER_NEAREST"]}
  
      mode_defines = {"constant": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_CLAMP"],
                      "wrap": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_REPEAT"],
                      "edge": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_CLAMP_TO_EDGE"]
                      }
  
      if not interpolation in interpolation_defines:
          raise KeyError(
              "interpolation = '%s' not defined ,valid: %s" % (interpolation, list(interpolation_defines.keys())))
  
      if not mode in mode_defines:
          raise KeyError("mode = '%s' not defined ,valid: %s" % (mode, list(mode_defines.keys())))
  
      # reorder matrix, such that x,y,z -> z,y,x (as the kernel is assuming that)
  
      d_im = OCLImage.from_array(data.astype(np.float32, copy=False))
      res_g = OCLArray.empty(data.shape, np.float32)
      mat_inv_g = OCLArray.from_array(mat.astype(np.float32, copy=False))
  
      prog = OCLProgram(abspath("kernels/affine.cl")
                        , build_options=interpolation_defines[interpolation] +
                                        mode_defines[mode])
  
      prog.run_kernel("affine3",
                      data.shape[::-1], None,
                      d_im, res_g.data, mat_inv_g.data)
  
      return res_g.get()