From 886d74aa7bddf2cf5972ab6516ace2dcb764e844 Mon Sep 17 00:00:00 2001
From: Edoardo Pasca <edo.paskino@gmail.com>
Date: Thu, 25 Jan 2018 15:41:36 +0000
Subject: added cython wrapper for gpu regularizers

---
 Wrappers/Python/setup.py                 |   22 +-
 Wrappers/Python/src/fista_module.cpp     | 1047 ++++++++++++++++++++++++++++++
 Wrappers/Python/src/fista_module_gpu.pyx |  154 +++++
 Wrappers/Python/src/multiply.pyx         |   49 ++
 4 files changed, 1271 insertions(+), 1 deletion(-)
 create mode 100644 Wrappers/Python/src/fista_module.cpp
 create mode 100644 Wrappers/Python/src/fista_module_gpu.pyx
 create mode 100644 Wrappers/Python/src/multiply.pyx

(limited to 'Wrappers/Python')

diff --git a/Wrappers/Python/setup.py b/Wrappers/Python/setup.py
index d2129b0..c535a34 100644
--- a/Wrappers/Python/setup.py
+++ b/Wrappers/Python/setup.py
@@ -53,13 +53,33 @@ else:
     else:
         extra_libraries += ['boost_python', 'boost_numpy','gomp']
 
+setup(
+    name='ccpi',
+	description='CCPi Core Imaging Library - Image Regularizers',
+	version=cil_version,
+    cmdclass = {'build_ext': build_ext},
+    ext_modules = [Extension("ccpi.filters.gpu_regularizers",
+                             sources=[ 
+                                     os.path.join("." , "src", "fista_module_gpu.pyx" ),
+                                     #os.path.join("." , "src", "multiply.pyx" )
+                                       ],
+                             include_dirs=extra_include_dirs, 
+							 library_dirs=extra_library_dirs, 
+							 extra_compile_args=extra_compile_args, 
+							 libraries=extra_libraries ), 
+    
+    ],
+	zip_safe = False,	
+	packages = {'ccpi','ccpi.filters'},
+)
+
 setup(
     name='ccpi',
 	description='CCPi Core Imaging Library - Image Regularizers',
 	version=cil_version,
     cmdclass = {'build_ext': build_ext},
     ext_modules = [Extension("ccpi.filters.cpu_regularizers",
-                             sources=[os.path.join("." , "fista_module.cpp" ),
+                             sources=[os.path.join("." , "src", "fista_module.cpp" ),
                                      # os.path.join("@CMAKE_SOURCE_DIR@" , "main_func" ,  "regularizers_CPU", "FGP_TV_core.c"),
 									 # os.path.join("@CMAKE_SOURCE_DIR@" , "main_func" ,  "regularizers_CPU", "SplitBregman_TV_core.c"),
 									 # os.path.join("@CMAKE_SOURCE_DIR@" , "main_func" ,  "regularizers_CPU", "LLT_model_core.c"),
diff --git a/Wrappers/Python/src/fista_module.cpp b/Wrappers/Python/src/fista_module.cpp
new file mode 100644
index 0000000..3876cad
--- /dev/null
+++ b/Wrappers/Python/src/fista_module.cpp
@@ -0,0 +1,1047 @@
+/*
+This work is part of the Core Imaging Library developed by
+Visual Analytics and Imaging System Group of the Science Technology
+Facilities Council, STFC
+
+Copyright 2017 Daniil Kazantsev
+Copyright 2017 Srikanth Nagella, Edoardo Pasca
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+
+#include <iostream>
+#include <cmath>
+
+#include <boost/python.hpp>
+#include <boost/python/numpy.hpp>
+#include "boost/tuple/tuple.hpp"
+
+#include "SplitBregman_TV_core.h"
+#include "FGP_TV_core.h"
+#include "LLT_model_core.h"
+#include "PatchBased_Regul_core.h"
+#include "TGV_PD_core.h"
+#include "utils.h"
+
+
+
+#if defined(_WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64)
+#include <windows.h>
+// this trick only if compiler is MSVC
+__if_not_exists(uint8_t) { typedef __int8 uint8_t; }
+__if_not_exists(uint16_t) { typedef __int8 uint16_t; }
+#endif
+
+namespace bp = boost::python;
+namespace np = boost::python::numpy;
+
+/*! in the Matlab implementation this is called as
+void mexFunction(
+int nlhs, mxArray *plhs[],
+int nrhs, const mxArray *prhs[])
+where:
+prhs Array of pointers to the INPUT mxArrays
+nrhs int number of INPUT mxArrays
+
+nlhs Array of pointers to the OUTPUT mxArrays
+plhs int number of OUTPUT mxArrays
+
+***********************************************************
+
+***********************************************************
+double mxGetScalar(const mxArray *pm);
+args: pm Pointer to an mxArray; cannot be a cell mxArray, a structure mxArray, or an empty mxArray.
+Returns: Pointer to the value of the first real (nonimaginary) element of the mxArray.	In C, mxGetScalar returns a double.
+***********************************************************
+char *mxArrayToString(const mxArray *array_ptr);
+args: array_ptr Pointer to mxCHAR array.
+Returns: C-style string. Returns NULL on failure. Possible reasons for failure include out of memory and specifying an array that is not an mxCHAR array.
+Description: Call mxArrayToString to copy the character data of an mxCHAR array into a C-style string.
+***********************************************************
+mxClassID mxGetClassID(const mxArray *pm);
+args: pm Pointer to an mxArray
+Returns: Numeric identifier of the class (category) of the mxArray that pm points to.For user-defined types,
+mxGetClassId returns a unique value identifying the class of the array contents.
+Use mxIsClass to determine whether an array is of a specific user-defined type.
+
+mxClassID Value	  MATLAB Type   MEX Type	 C Primitive Type
+mxINT8_CLASS 	  int8	        int8_T	     char, byte
+mxUINT8_CLASS	  uint8	        uint8_T	     unsigned char, byte
+mxINT16_CLASS	  int16	        int16_T	     short
+mxUINT16_CLASS	  uint16	    uint16_T	 unsigned short
+mxINT32_CLASS	  int32	        int32_T	     int
+mxUINT32_CLASS	  uint32	    uint32_T	 unsigned int
+mxINT64_CLASS	  int64	        int64_T	     long long
+mxUINT64_CLASS	  uint64	    uint64_T 	 unsigned long long
+mxSINGLE_CLASS	  single	    float	     float
+mxDOUBLE_CLASS	  double	    double	     double
+
+****************************************************************
+double *mxGetPr(const mxArray *pm);
+args: pm Pointer to an mxArray of type double
+Returns: Pointer to the first element of the real data. Returns NULL in C (0 in Fortran) if there is no real data.
+****************************************************************
+mxArray *mxCreateNumericArray(mwSize ndim, const mwSize *dims,
+mxClassID classid, mxComplexity ComplexFlag);
+args: ndimNumber of dimensions. If you specify a value for ndim that is less than 2, mxCreateNumericArray automatically sets the number of dimensions to 2.
+dims Dimensions array. Each element in the dimensions array contains the size of the array in that dimension.
+For example, in C, setting dims[0] to 5 and dims[1] to 7 establishes a 5-by-7 mxArray. Usually there are ndim elements in the dims array.
+classid Identifier for the class of the array, which determines the way the numerical data is represented in memory.
+For example, specifying mxINT16_CLASS in C causes each piece of numerical data in the mxArray to be represented as a 16-bit signed integer.
+ComplexFlag  If the mxArray you are creating is to contain imaginary data, set ComplexFlag to mxCOMPLEX in C (1 in Fortran). Otherwise, set ComplexFlag to mxREAL in C (0 in Fortran).
+Returns: Pointer to the created mxArray, if successful. If unsuccessful in a standalone (non-MEX file) application, returns NULL in C (0 in Fortran).
+If unsuccessful in a MEX file, the MEX file terminates and returns control to the MATLAB prompt. The function is unsuccessful when there is not
+enough free heap space to create the mxArray.
+*/
+
+
+
+bp::list SplitBregman_TV(np::ndarray input, double d_mu, int iter, double d_epsil, int methTV) {
+	
+	// the result is in the following list
+	bp::list result;
+		
+	int number_of_dims, dimX, dimY, dimZ, ll, j, count;
+	//const int  *dim_array;
+	float *A, *U = NULL, *U_old = NULL, *Dx = NULL, *Dy = NULL, *Dz = NULL, *Bx = NULL, *By = NULL, *Bz = NULL, lambda, mu, epsil, re, re1, re_old;
+	
+	//number_of_dims = mxGetNumberOfDimensions(prhs[0]);
+	//dim_array = mxGetDimensions(prhs[0]);
+
+	number_of_dims = input.get_nd();
+	int dim_array[3];
+
+	dim_array[0] = input.shape(0);
+	dim_array[1] = input.shape(1);
+	if (number_of_dims == 2) {
+		dim_array[2] = -1;
+	}
+	else {
+		dim_array[2] = input.shape(2);
+	}
+
+	// Parameter handling is be done in Python
+	///*Handling Matlab input data*/
+	//if ((nrhs < 2) || (nrhs > 5)) mexErrMsgTxt("At least 2 parameters is required: Image(2D/3D), Regularization parameter. The full list of parameters: Image(2D/3D), Regularization parameter, iterations number, tolerance, penalty type ('iso' or 'l1')");
+
+	///*Handling Matlab input data*/
+	//A = (float *)mxGetData(prhs[0]); /*noisy image (2D/3D) */
+	A = reinterpret_cast<float *>(input.get_data());
+
+	//mu = (float)mxGetScalar(prhs[1]); /* regularization parameter */
+	mu = (float)d_mu;
+
+	//iter = 35; /* default iterations number */
+	
+	//epsil = 0.0001; /* default tolerance constant */
+	epsil = (float)d_epsil;
+	//methTV = 0;  /* default isotropic TV penalty */
+	//if ((nrhs == 3) || (nrhs == 4) || (nrhs == 5))  iter = (int)mxGetScalar(prhs[2]); /* iterations number */
+	//if ((nrhs == 4) || (nrhs == 5))  epsil = (float)mxGetScalar(prhs[3]); /* tolerance constant */
+	//if (nrhs == 5) {
+	//	char *penalty_type;
+	//	penalty_type = mxArrayToString(prhs[4]); /* choosing TV penalty: 'iso' or 'l1', 'iso' is the default */
+	//	if ((strcmp(penalty_type, "l1") != 0) && (strcmp(penalty_type, "iso") != 0)) mexErrMsgTxt("Choose TV type: 'iso' or 'l1',");
+	//	if (strcmp(penalty_type, "l1") == 0)  methTV = 1;  /* enable 'l1' penalty */
+	//	mxFree(penalty_type);
+	//}
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input image must be in a single precision"); }
+
+	lambda = 2.0f*mu;
+	count = 1;
+	re_old = 0.0f;
+	/*Handling Matlab output data*/
+	dimY = dim_array[0]; dimX = dim_array[1]; dimZ = dim_array[2];
+
+	if (number_of_dims == 2) {
+		dimZ = 1; /*2D case*/
+		//U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		//U_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		//Dx = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		//Dy = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		//Bx = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		//By = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+		np::ndarray npU = np::zeros(shape, dtype);
+		np::ndarray npU_old = np::zeros(shape, dtype);
+		np::ndarray npDx = np::zeros(shape, dtype);
+		np::ndarray npDy = np::zeros(shape, dtype);
+		np::ndarray npBx = np::zeros(shape, dtype);
+		np::ndarray npBy = np::zeros(shape, dtype);
+
+		U = reinterpret_cast<float *>(npU.get_data());
+		U_old = reinterpret_cast<float *>(npU_old.get_data());
+		Dx = reinterpret_cast<float *>(npDx.get_data());
+		Dy = reinterpret_cast<float *>(npDy.get_data());
+		Bx = reinterpret_cast<float *>(npBx.get_data());
+		By = reinterpret_cast<float *>(npBy.get_data());
+
+
+
+		copyIm(A, U, dimX, dimY, dimZ); /*initialize */
+
+										/* begin outer SB iterations */
+		for (ll = 0; ll < iter; ll++) {
+
+			/*storing old values*/
+			copyIm(U, U_old, dimX, dimY, dimZ);
+
+			/*GS iteration */
+			gauss_seidel2D(U, A, Dx, Dy, Bx, By, dimX, dimY, lambda, mu);
+
+			if (methTV == 1)  updDxDy_shrinkAniso2D(U, Dx, Dy, Bx, By, dimX, dimY, lambda);
+			else updDxDy_shrinkIso2D(U, Dx, Dy, Bx, By, dimX, dimY, lambda);
+
+			updBxBy2D(U, Dx, Dy, Bx, By, dimX, dimY);
+
+			/* calculate norm to terminate earlier */
+			re = 0.0f; re1 = 0.0f;
+			for (j = 0; j < dimX*dimY*dimZ; j++)
+			{
+				re += pow(U_old[j] - U[j], 2);
+				re1 += pow(U_old[j], 2);
+			}
+			re = sqrt(re) / sqrt(re1);
+			if (re < epsil)  count++;
+			if (count > 4) break;
+
+			/* check that the residual norm is decreasing */
+			if (ll > 2) {
+				if (re > re_old) break;
+			}
+			re_old = re;
+			/*printf("%f %i %i \n", re, ll, count); */
+
+			/*copyIm(U_old, U, dimX, dimY, dimZ); */
+			
+		}
+		//printf("SB iterations stopped at iteration: %i\n", ll);
+		result.append<np::ndarray>(npU);
+		result.append<int>(ll);
+	}
+	if (number_of_dims == 3) {
+			/*U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			U_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			Dx = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			Dy = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			Dz = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			Bx = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			By = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+			Bz = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));*/
+			bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1], dim_array[2]);
+			np::dtype dtype = np::dtype::get_builtin<float>();
+
+			np::ndarray npU     = np::zeros(shape, dtype);
+			np::ndarray npU_old = np::zeros(shape, dtype);
+			np::ndarray npDx    = np::zeros(shape, dtype);
+			np::ndarray npDy    = np::zeros(shape, dtype);
+			np::ndarray npDz    = np::zeros(shape, dtype);
+			np::ndarray npBx    = np::zeros(shape, dtype);
+			np::ndarray npBy    = np::zeros(shape, dtype);
+			np::ndarray npBz    = np::zeros(shape, dtype);
+
+			U     = reinterpret_cast<float *>(npU.get_data());
+			U_old = reinterpret_cast<float *>(npU_old.get_data());
+			Dx    = reinterpret_cast<float *>(npDx.get_data());
+			Dy    = reinterpret_cast<float *>(npDy.get_data());
+			Dz    = reinterpret_cast<float *>(npDz.get_data());
+			Bx    = reinterpret_cast<float *>(npBx.get_data());
+			By    = reinterpret_cast<float *>(npBy.get_data());
+			Bz    = reinterpret_cast<float *>(npBz.get_data());
+
+			copyIm(A, U, dimX, dimY, dimZ); /*initialize */
+
+											/* begin outer SB iterations */
+			for (ll = 0; ll<iter; ll++) {
+
+				/*storing old values*/
+				copyIm(U, U_old, dimX, dimY, dimZ);
+
+				/*GS iteration */
+				gauss_seidel3D(U, A, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ, lambda, mu);
+
+				if (methTV == 1) updDxDyDz_shrinkAniso3D(U, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ, lambda);
+				else updDxDyDz_shrinkIso3D(U, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ, lambda);
+
+				updBxByBz3D(U, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ);
+
+				/* calculate norm to terminate earlier */
+				re = 0.0f; re1 = 0.0f;
+				for (j = 0; j<dimX*dimY*dimZ; j++)
+				{
+					re += pow(U[j] - U_old[j], 2);
+					re1 += pow(U[j], 2);
+				}
+				re = sqrt(re) / sqrt(re1);
+				if (re < epsil)  count++;
+				if (count > 4) break;
+
+				/* check that the residual norm is decreasing */
+				if (ll > 2) {
+					if (re > re_old) break;
+				}
+				/*printf("%f %i %i \n", re, ll, count); */
+				re_old = re;
+			}
+			//printf("SB iterations stopped at iteration: %i\n", ll);
+			result.append<np::ndarray>(npU);
+			result.append<int>(ll);
+		}
+	return result;
+
+	}
+
+
+
+bp::list FGP_TV(np::ndarray input, double d_mu, int iter, double d_epsil, int methTV) {
+
+	// the result is in the following list
+	bp::list result;
+
+	int number_of_dims, dimX, dimY, dimZ, ll, j, count;
+	float *A, *D = NULL, *D_old = NULL, *P1 = NULL, *P2 = NULL, *P3 = NULL, *P1_old = NULL, *P2_old = NULL, *P3_old = NULL, *R1 = NULL, *R2 = NULL, *R3 = NULL;
+	float lambda, tk, tkp1, re, re1, re_old, epsil, funcval;
+
+	//number_of_dims = mxGetNumberOfDimensions(prhs[0]);
+	//dim_array = mxGetDimensions(prhs[0]);
+
+	number_of_dims = input.get_nd();
+	int dim_array[3];
+
+	dim_array[0] = input.shape(0);
+	dim_array[1] = input.shape(1);
+	if (number_of_dims == 2) {
+		dim_array[2] = -1;
+	}
+	else {
+		dim_array[2] = input.shape(2);
+	}
+	// Parameter handling is be done in Python
+	///*Handling Matlab input data*/
+	//if ((nrhs < 2) || (nrhs > 5)) mexErrMsgTxt("At least 2 parameters is required: Image(2D/3D), Regularization parameter. The full list of parameters: Image(2D/3D), Regularization parameter, iterations number, tolerance, penalty type ('iso' or 'l1')");
+
+	///*Handling Matlab input data*/
+	//A = (float *)mxGetData(prhs[0]); /*noisy image (2D/3D) */
+	A = reinterpret_cast<float *>(input.get_data());
+
+	//mu = (float)mxGetScalar(prhs[1]); /* regularization parameter */
+	lambda = (float)d_mu;
+
+	//iter = 35; /* default iterations number */
+
+	//epsil = 0.0001; /* default tolerance constant */
+	epsil = (float)d_epsil;
+	//methTV = 0;  /* default isotropic TV penalty */
+	//if ((nrhs == 3) || (nrhs == 4) || (nrhs == 5))  iter = (int)mxGetScalar(prhs[2]); /* iterations number */
+	//if ((nrhs == 4) || (nrhs == 5))  epsil = (float)mxGetScalar(prhs[3]); /* tolerance constant */
+	//if (nrhs == 5) {
+	//	char *penalty_type;
+	//	penalty_type = mxArrayToString(prhs[4]); /* choosing TV penalty: 'iso' or 'l1', 'iso' is the default */
+	//	if ((strcmp(penalty_type, "l1") != 0) && (strcmp(penalty_type, "iso") != 0)) mexErrMsgTxt("Choose TV type: 'iso' or 'l1',");
+	//	if (strcmp(penalty_type, "l1") == 0)  methTV = 1;  /* enable 'l1' penalty */
+	//	mxFree(penalty_type);
+	//}
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input image must be in a single precision"); }
+
+	//plhs[1] = mxCreateNumericMatrix(1, 1, mxSINGLE_CLASS, mxREAL);
+	bp::tuple shape1 = bp::make_tuple(dim_array[0], dim_array[1]);
+	np::dtype dtype = np::dtype::get_builtin<float>();
+	np::ndarray out1 = np::zeros(shape1, dtype);
+	
+	//float *funcvalA = (float *)mxGetData(plhs[1]);
+	float * funcvalA = reinterpret_cast<float *>(out1.get_data());
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input image must be in a single precision"); }
+
+	/*Handling Matlab output data*/
+	dimX = dim_array[0]; dimY = dim_array[1]; dimZ = dim_array[2];
+
+	tk = 1.0f;
+	tkp1 = 1.0f;
+	count = 1;
+	re_old = 0.0f;
+
+	if (number_of_dims == 2) {
+		dimZ = 1; /*2D case*/
+		/*D = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		D_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		P1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		P2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		P1_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		P2_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		R1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		R2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));*/
+
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+
+		np::ndarray npD      = np::zeros(shape, dtype);
+		np::ndarray npD_old  = np::zeros(shape, dtype);
+		np::ndarray npP1     = np::zeros(shape, dtype);
+		np::ndarray npP2     = np::zeros(shape, dtype);
+		np::ndarray npP1_old = np::zeros(shape, dtype);
+		np::ndarray npP2_old = np::zeros(shape, dtype);
+		np::ndarray npR1     = np::zeros(shape, dtype);
+		np::ndarray npR2     = np::zeros(shape, dtype);
+
+		D      = reinterpret_cast<float *>(npD.get_data());
+		D_old  = reinterpret_cast<float *>(npD_old.get_data());
+		P1     = reinterpret_cast<float *>(npP1.get_data());
+		P2     = reinterpret_cast<float *>(npP2.get_data());
+		P1_old = reinterpret_cast<float *>(npP1_old.get_data());
+		P2_old = reinterpret_cast<float *>(npP2_old.get_data());
+		R1     = reinterpret_cast<float *>(npR1.get_data());
+		R2     = reinterpret_cast<float *>(npR2.get_data());
+
+		/* begin iterations */
+		for (ll = 0; ll<iter; ll++) {
+			/* computing the gradient of the objective function */
+			Obj_func2D(A, D, R1, R2, lambda, dimX, dimY);
+
+			/*Taking a step towards minus of the gradient*/
+			Grad_func2D(P1, P2, D, R1, R2, lambda, dimX, dimY);
+
+
+
+
+			/*updating R and t*/
+			tkp1 = (1.0f + sqrt(1.0f + 4.0f*tk*tk))*0.5f;
+			Rupd_func2D(P1, P1_old, P2, P2_old, R1, R2, tkp1, tk, dimX, dimY);
+
+			/* calculate norm */
+			re = 0.0f; re1 = 0.0f;
+			for (j = 0; j<dimX*dimY*dimZ; j++)
+			{
+				re += pow(D[j] - D_old[j], 2);
+				re1 += pow(D[j], 2);
+			}
+			re = sqrt(re) / sqrt(re1);
+			if (re < epsil)  count++;
+			if (count > 3) {
+				Obj_func2D(A, D, P1, P2, lambda, dimX, dimY);
+				funcval = 0.0f;
+				for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+				//funcvalA[0] = sqrt(funcval);
+				float fv = sqrt(funcval);
+				std::memcpy(funcvalA, &fv, sizeof(float));
+				break;
+			}
+
+			/* check that the residual norm is decreasing */
+			if (ll > 2) {
+				if (re > re_old) {
+					Obj_func2D(A, D, P1, P2, lambda, dimX, dimY);
+					funcval = 0.0f;
+					for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+					//funcvalA[0] = sqrt(funcval);
+					float fv = sqrt(funcval);
+					std::memcpy(funcvalA, &fv, sizeof(float));
+					break;
+				}
+			}
+			re_old = re;
+			/*printf("%f %i %i \n", re, ll, count); */
+
+			/*storing old values*/
+			copyIm(D, D_old, dimX, dimY, dimZ);
+			copyIm(P1, P1_old, dimX, dimY, dimZ);
+			copyIm(P2, P2_old, dimX, dimY, dimZ);
+			tk = tkp1;
+
+			/* calculating the objective function value */
+			if (ll == (iter - 1)) {
+				Obj_func2D(A, D, P1, P2, lambda, dimX, dimY);
+				funcval = 0.0f;
+				for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+				//funcvalA[0] = sqrt(funcval);
+				float fv = sqrt(funcval);
+				std::memcpy(funcvalA, &fv, sizeof(float));
+			}
+		}
+		//printf("FGP-TV iterations stopped at iteration %i with the function value %f \n", ll, funcvalA[0]);
+		result.append<np::ndarray>(npD);
+		result.append<np::ndarray>(out1);
+		result.append<int>(ll);
+	}
+	if (number_of_dims == 3) {
+		/*D = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		D_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P1_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P2_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		P3_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		R1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		R2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		R3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));*/
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1], dim_array[2]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+		
+		np::ndarray npD      = np::zeros(shape, dtype);
+		np::ndarray npD_old  = np::zeros(shape, dtype);
+		np::ndarray npP1     = np::zeros(shape, dtype);
+		np::ndarray npP2     = np::zeros(shape, dtype);
+		np::ndarray npP3     = np::zeros(shape, dtype);
+		np::ndarray npP1_old = np::zeros(shape, dtype);
+		np::ndarray npP2_old = np::zeros(shape, dtype);
+		np::ndarray npP3_old = np::zeros(shape, dtype);
+		np::ndarray npR1     = np::zeros(shape, dtype);
+		np::ndarray npR2     = np::zeros(shape, dtype);
+		np::ndarray npR3     = np::zeros(shape, dtype);
+
+		D      = reinterpret_cast<float *>(npD.get_data());
+		D_old  = reinterpret_cast<float *>(npD_old.get_data());
+		P1     = reinterpret_cast<float *>(npP1.get_data());
+		P2     = reinterpret_cast<float *>(npP2.get_data());
+		P3     = reinterpret_cast<float *>(npP3.get_data());
+		P1_old = reinterpret_cast<float *>(npP1_old.get_data());
+		P2_old = reinterpret_cast<float *>(npP2_old.get_data());
+		P3_old = reinterpret_cast<float *>(npP3_old.get_data());
+		R1     = reinterpret_cast<float *>(npR1.get_data());
+		R2     = reinterpret_cast<float *>(npR2.get_data());
+		R3     = reinterpret_cast<float *>(npR3.get_data());
+		/* begin iterations */
+		for (ll = 0; ll<iter; ll++) {
+			/* computing the gradient of the objective function */
+			Obj_func3D(A, D, R1, R2, R3, lambda, dimX, dimY, dimZ);
+			/*Taking a step towards minus of the gradient*/
+			Grad_func3D(P1, P2, P3, D, R1, R2, R3, lambda, dimX, dimY, dimZ);
+
+			/* projection step */
+			Proj_func3D(P1, P2, P3, dimX, dimY, dimZ);
+
+			/*updating R and t*/
+			tkp1 = (1.0f + sqrt(1.0f + 4.0f*tk*tk))*0.5f;
+			Rupd_func3D(P1, P1_old, P2, P2_old, P3, P3_old, R1, R2, R3, tkp1, tk, dimX, dimY, dimZ);
+
+			/* calculate norm - stopping rules*/
+			re = 0.0f; re1 = 0.0f;
+			for (j = 0; j<dimX*dimY*dimZ; j++)
+			{
+				re += pow(D[j] - D_old[j], 2);
+				re1 += pow(D[j], 2);
+			}
+			re = sqrt(re) / sqrt(re1);
+			/* stop if the norm residual is less than the tolerance EPS */
+			if (re < epsil)  count++;
+			if (count > 3) {
+				Obj_func3D(A, D, P1, P2, P3, lambda, dimX, dimY, dimZ);
+				funcval = 0.0f;
+				for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+				//funcvalA[0] = sqrt(funcval);
+				float fv = sqrt(funcval);
+				std::memcpy(funcvalA, &fv, sizeof(float));
+				break;
+			}
+
+			/* check that the residual norm is decreasing */
+			if (ll > 2) {
+				if (re > re_old) {
+					Obj_func3D(A, D, P1, P2, P3, lambda, dimX, dimY, dimZ);
+					funcval = 0.0f;
+					for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+					//funcvalA[0] = sqrt(funcval);
+					float fv = sqrt(funcval);
+					std::memcpy(funcvalA, &fv, sizeof(float));
+					break;
+				}
+			}
+
+			re_old = re;
+			/*printf("%f %i %i \n", re, ll, count); */
+
+			/*storing old values*/
+			copyIm(D, D_old, dimX, dimY, dimZ);
+			copyIm(P1, P1_old, dimX, dimY, dimZ);
+			copyIm(P2, P2_old, dimX, dimY, dimZ);
+			copyIm(P3, P3_old, dimX, dimY, dimZ);
+			tk = tkp1;
+
+			if (ll == (iter - 1)) {
+				Obj_func3D(A, D, P1, P2, P3, lambda, dimX, dimY, dimZ);
+				funcval = 0.0f;
+				for (j = 0; j<dimX*dimY*dimZ; j++) funcval += pow(D[j], 2);
+				//funcvalA[0] = sqrt(funcval);
+				float fv = sqrt(funcval);
+				std::memcpy(funcvalA, &fv, sizeof(float));
+			}
+
+		}
+		//printf("FGP-TV iterations stopped at iteration %i with the function value %f \n", ll, funcvalA[0]);
+		result.append<np::ndarray>(npD);
+		result.append<np::ndarray>(out1);
+		result.append<int>(ll);
+	}
+
+	return result;
+}
+
+bp::list LLT_model(np::ndarray input, double d_lambda, double d_tau, int iter, double d_epsil, int switcher) {
+	// the result is in the following list
+	bp::list result;
+	
+	int number_of_dims, dimX, dimY, dimZ, ll, j, count;
+	//const int  *dim_array;
+	float *U0, *U = NULL, *U_old = NULL, *D1 = NULL, *D2 = NULL, *D3 = NULL, lambda, tau, re, re1, epsil, re_old;
+	unsigned short *Map = NULL;
+
+	number_of_dims = input.get_nd();
+	int dim_array[3];
+
+	dim_array[0] = input.shape(0);
+	dim_array[1] = input.shape(1);
+	if (number_of_dims == 2) {
+		dim_array[2] = -1;
+	}
+	else {
+		dim_array[2] = input.shape(2);
+	}
+
+	///*Handling Matlab input data*/
+	//U0 = (float *)mxGetData(prhs[0]); /*origanal noise image/volume*/
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input in single precision is required"); }
+	//lambda = (float)mxGetScalar(prhs[1]); /*regularization parameter*/
+	//tau = (float)mxGetScalar(prhs[2]); /* time-step */
+	//iter = (int)mxGetScalar(prhs[3]); /*iterations number*/
+	//epsil = (float)mxGetScalar(prhs[4]); /* tolerance constant */
+	//switcher = (int)mxGetScalar(prhs[5]); /*switch on (1) restrictive smoothing in Z dimension*/
+	
+	U0 = reinterpret_cast<float *>(input.get_data());
+	lambda = (float)d_lambda;
+	tau = (float)d_tau;
+	// iter is passed as parameter
+	epsil = (float)d_epsil;
+	// switcher is passed as parameter
+										  /*Handling Matlab output data*/
+	dimX = dim_array[0]; dimY = dim_array[1];  dimZ = 1;
+
+	if (number_of_dims == 2) {
+		/*2D case*/
+		/*U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		U_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		D1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		D2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));*/
+
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+
+		np::ndarray npU = np::zeros(shape, dtype);
+		np::ndarray npU_old = np::zeros(shape, dtype);
+		np::ndarray npD1 = np::zeros(shape, dtype);
+		np::ndarray npD2 = np::zeros(shape, dtype);
+		
+        //result.append<np::ndarray>(npU);
+		
+		U = reinterpret_cast<float *>(npU.get_data());
+		U_old = reinterpret_cast<float *>(npU_old.get_data());
+		D1 = reinterpret_cast<float *>(npD1.get_data());
+		D2 = reinterpret_cast<float *>(npD2.get_data());
+		
+		/*Copy U0 to U*/
+		copyIm(U0, U, dimX, dimY, dimZ);
+
+		count = 1;
+		re_old = 0.0f;
+
+		for (ll = 0; ll < iter; ll++) {
+			copyIm(U, U_old, dimX, dimY, dimZ);
+
+			/*estimate inner derrivatives */
+			der2D(U, D1, D2, dimX, dimY, dimZ);
+			/* calculate div^2 and update */
+			div_upd2D(U0, U, D1, D2, dimX, dimY, dimZ, lambda, tau);
+
+			/* calculate norm to terminate earlier */
+			re = 0.0f; re1 = 0.0f;
+			for (j = 0; j<dimX*dimY*dimZ; j++)
+			{
+				re += pow(U_old[j] - U[j], 2);
+				re1 += pow(U_old[j], 2);
+			}
+			re = sqrt(re) / sqrt(re1);
+			if (re < epsil)  count++;
+			if (count > 4) break;
+
+			/* check that the residual norm is decreasing */
+			if (ll > 2) {
+				if (re > re_old) break;
+			}
+			re_old = re;
+
+		} /*end of iterations*/
+		//  printf("HO iterations stopped at iteration: %i\n", ll);
+		result.append<np::ndarray>(npU);
+		
+	}
+	else if (number_of_dims == 3) {
+		/*3D case*/
+		dimZ = dim_array[2];
+		/*U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		U_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		D1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		D2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		D3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL));
+		if (switcher != 0) {
+			Map = (unsigned short*)mxGetPr(plhs[1] = mxCreateNumericArray(3, dim_array, mxUINT16_CLASS, mxREAL));
+		}*/
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1], dim_array[2]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+
+		np::ndarray npU = np::zeros(shape, dtype);
+		np::ndarray npU_old = np::zeros(shape, dtype);
+		np::ndarray npD1 = np::zeros(shape, dtype);
+		np::ndarray npD2 = np::zeros(shape, dtype);
+		np::ndarray npD3 = np::zeros(shape, dtype);
+		np::ndarray npMap = np::zeros(shape, np::dtype::get_builtin<unsigned short>());
+		Map = reinterpret_cast<unsigned short *>(npMap.get_data());
+		if (switcher != 0) {
+			//Map = (unsigned short*)mxGetPr(plhs[1] = mxCreateNumericArray(3, dim_array, mxUINT16_CLASS, mxREAL));
+			
+			Map = reinterpret_cast<unsigned short *>(npMap.get_data());
+		}
+
+		U = reinterpret_cast<float *>(npU.get_data());
+		U_old = reinterpret_cast<float *>(npU_old.get_data());
+		D1 = reinterpret_cast<float *>(npD1.get_data());
+		D2 = reinterpret_cast<float *>(npD2.get_data());
+		D3 = reinterpret_cast<float *>(npD2.get_data());
+		
+		/*Copy U0 to U*/
+		copyIm(U0, U, dimX, dimY, dimZ);
+
+		count = 1;
+		re_old = 0.0f;
+	
+
+		if (switcher == 1) {
+			/* apply restrictive smoothing */
+			calcMap(U, Map, dimX, dimY, dimZ);
+			/*clear outliers */
+			cleanMap(Map, dimX, dimY, dimZ);
+		}
+		for (ll = 0; ll < iter; ll++) {
+
+			copyIm(U, U_old, dimX, dimY, dimZ);
+
+			/*estimate inner derrivatives */
+			der3D(U, D1, D2, D3, dimX, dimY, dimZ);
+			/* calculate div^2 and update */
+			div_upd3D(U0, U, D1, D2, D3, Map, switcher, dimX, dimY, dimZ, lambda, tau);
+
+			/* calculate norm to terminate earlier */
+			re = 0.0f; re1 = 0.0f;
+			for (j = 0; j<dimX*dimY*dimZ; j++)
+			{
+				re += pow(U_old[j] - U[j], 2);
+				re1 += pow(U_old[j], 2);
+			}
+			re = sqrt(re) / sqrt(re1);
+			if (re < epsil)  count++;
+			if (count > 4) break;
+
+			/* check that the residual norm is decreasing */
+			if (ll > 2) {
+				if (re > re_old) break;
+			}
+			re_old = re;
+
+		} /*end of iterations*/
+		//printf("HO iterations stopped at iteration: %i\n", ll);
+		result.append<np::ndarray>(npU);
+		if (switcher != 0) result.append<np::ndarray>(npMap);
+
+	}
+	return result;
+}
+
+
+bp::list PatchBased_Regul(np::ndarray input, double d_lambda, int SearchW_real, int SimilW,  double d_h) {
+	// the result is in the following list
+	bp::list result;
+
+	int N, M, Z, numdims, SearchW, /*SimilW, SearchW_real,*/ padXY, newsizeX, newsizeY, newsizeZ, switchpad_crop;
+	//const int  *dims;
+	float *A, *B = NULL, *Ap = NULL, *Bp = NULL, h, lambda;
+
+	numdims = input.get_nd();
+	int dims[3];
+
+	dims[0] = input.shape(0);
+	dims[1] = input.shape(1);
+	if (numdims == 2) {
+		dims[2] = -1;
+	}
+	else {
+		dims[2] = input.shape(2);
+	}
+	/*numdims = mxGetNumberOfDimensions(prhs[0]);
+	dims = mxGetDimensions(prhs[0]);*/
+
+	N = dims[0];
+	M = dims[1];
+	Z = dims[2];
+
+	//if ((numdims < 2) || (numdims > 3)) { mexErrMsgTxt("The input should be 2D image or 3D volume"); }
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input in single precision is required"); }
+
+	//if (nrhs != 5) mexErrMsgTxt("Five inputs reqired: Image(2D,3D), SearchW, SimilW, Threshold, Regularization parameter");
+
+	///*Handling inputs*/
+	//A = (float *)mxGetData(prhs[0]);    /* the image to regularize/filter */
+	A = reinterpret_cast<float *>(input.get_data());
+	//SearchW_real = (int)mxGetScalar(prhs[1]); /* the searching window ratio */
+	//SimilW = (int)mxGetScalar(prhs[2]);  /* the similarity window ratio */
+	//h = (float)mxGetScalar(prhs[3]);  /* parameter for the PB filtering function */
+	//lambda = (float)mxGetScalar(prhs[4]); /* regularization parameter */
+
+	//if (h <= 0) mexErrMsgTxt("Parmeter for the PB penalty function should be > 0");
+	//if (lambda <= 0) mexErrMsgTxt(" Regularization parmeter should be > 0");
+
+	lambda = (float)d_lambda;
+	h = (float)d_h;
+	SearchW = SearchW_real + 2 * SimilW;
+
+	/* SearchW_full = 2*SearchW + 1; */ /* the full searching window  size */
+										/* SimilW_full = 2*SimilW + 1;  */  /* the full similarity window  size */
+
+
+	padXY = SearchW + 2 * SimilW; /* padding sizes */
+	newsizeX = N + 2 * (padXY); /* the X size of the padded array */
+	newsizeY = M + 2 * (padXY); /* the Y size of the padded array */
+	newsizeZ = Z + 2 * (padXY); /* the Z size of the padded array */
+	int N_dims[] = { newsizeX, newsizeY, newsizeZ };
+	/******************************2D case ****************************/
+	if (numdims == 2) {
+		///*Handling output*/
+		//B = (float*)mxGetData(plhs[0] = mxCreateNumericMatrix(N, M, mxSINGLE_CLASS, mxREAL));
+		///*allocating memory for the padded arrays */
+		//Ap = (float*)mxGetData(mxCreateNumericMatrix(newsizeX, newsizeY, mxSINGLE_CLASS, mxREAL));
+		//Bp = (float*)mxGetData(mxCreateNumericMatrix(newsizeX, newsizeY, mxSINGLE_CLASS, mxREAL));
+		///**************************************************************************/
+
+		bp::tuple shape = bp::make_tuple(N, M);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+		np::ndarray npB = np::zeros(shape, dtype);
+
+		shape = bp::make_tuple(newsizeX, newsizeY);
+		np::ndarray npAp = np::zeros(shape, dtype);
+		np::ndarray npBp = np::zeros(shape, dtype);
+		B = reinterpret_cast<float *>(npB.get_data());
+		Ap = reinterpret_cast<float *>(npAp.get_data());
+		Bp = reinterpret_cast<float *>(npBp.get_data());		
+
+		/*Perform padding of image A to the size of [newsizeX * newsizeY] */
+		switchpad_crop = 0; /*padding*/
+		pad_crop(A, Ap, M, N, 0, newsizeY, newsizeX, 0, padXY, switchpad_crop);
+		
+		/* Do PB regularization with the padded array  */
+		PB_FUNC2D(Ap, Bp, newsizeY, newsizeX, padXY, SearchW, SimilW, (float)h, (float)lambda);
+		
+		switchpad_crop = 1; /*cropping*/
+		pad_crop(Bp, B, M, N, 0, newsizeY, newsizeX, 0, padXY, switchpad_crop);
+		
+		result.append<np::ndarray>(npB);
+	}
+	else
+	{
+		/******************************3D case ****************************/
+		///*Handling output*/
+		//B = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dims, mxSINGLE_CLASS, mxREAL));
+		///*allocating memory for the padded arrays */
+		//Ap = (float*)mxGetPr(mxCreateNumericArray(3, N_dims, mxSINGLE_CLASS, mxREAL));
+		//Bp = (float*)mxGetPr(mxCreateNumericArray(3, N_dims, mxSINGLE_CLASS, mxREAL));
+		/**************************************************************************/
+		bp::tuple shape = bp::make_tuple(dims[0], dims[1], dims[2]);
+		bp::tuple shape_AB = bp::make_tuple(N_dims[0], N_dims[1], N_dims[2]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+		np::ndarray npB = np::zeros(shape, dtype);
+		np::ndarray npAp = np::zeros(shape_AB, dtype);
+		np::ndarray npBp = np::zeros(shape_AB, dtype);
+		B = reinterpret_cast<float *>(npB.get_data());
+		Ap = reinterpret_cast<float *>(npAp.get_data());
+		Bp = reinterpret_cast<float *>(npBp.get_data());
+		/*Perform padding of image A to the size of [newsizeX * newsizeY * newsizeZ] */
+		switchpad_crop = 0; /*padding*/
+		pad_crop(A, Ap, M, N, Z, newsizeY, newsizeX, newsizeZ, padXY, switchpad_crop);
+
+		/* Do PB regularization with the padded array  */
+		PB_FUNC3D(Ap, Bp, newsizeY, newsizeX, newsizeZ, padXY, SearchW, SimilW, (float)h, (float)lambda);
+
+		switchpad_crop = 1; /*cropping*/
+		pad_crop(Bp, B, M, N, Z, newsizeY, newsizeX, newsizeZ, padXY, switchpad_crop);
+
+		result.append<np::ndarray>(npB);
+	} /*end else ndims*/
+
+	return result;
+}
+
+bp::list TGV_PD(np::ndarray input, double d_lambda, double d_alpha1, double d_alpha0, int iter) {
+	// the result is in the following list
+	bp::list result;
+	int number_of_dims, /*iter,*/ dimX, dimY, dimZ, ll;
+	//const int  *dim_array;
+	float *A, *U, *U_old, *P1, *P2, *Q1, *Q2, *Q3, *V1, *V1_old, *V2, *V2_old, lambda, L2, tau, sigma, alpha1, alpha0;
+
+	//number_of_dims = mxGetNumberOfDimensions(prhs[0]);
+	//dim_array = mxGetDimensions(prhs[0]);
+	number_of_dims = input.get_nd();
+	int dim_array[3];
+
+	dim_array[0] = input.shape(0);
+	dim_array[1] = input.shape(1);
+	if (number_of_dims == 2) {
+		dim_array[2] = -1;
+	}
+	else {
+		dim_array[2] = input.shape(2);
+	}
+	/*Handling Matlab input data*/
+	//A = (float *)mxGetData(prhs[0]); /*origanal noise image/volume*/
+	//if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) { mexErrMsgTxt("The input in single precision is required"); }
+	
+	A = reinterpret_cast<float *>(input.get_data());
+
+	//lambda = (float)mxGetScalar(prhs[1]); /*regularization parameter*/
+	//alpha1 = (float)mxGetScalar(prhs[2]); /*first-order term*/
+	//alpha0 = (float)mxGetScalar(prhs[3]); /*second-order term*/
+	//iter = (int)mxGetScalar(prhs[4]); /*iterations number*/
+	//if (nrhs != 5) mexErrMsgTxt("Five input parameters is reqired: Image(2D/3D), Regularization parameter, alpha1, alpha0, Iterations");
+	lambda = (float)d_lambda;
+	alpha1 = (float)d_alpha1;
+	alpha0 = (float)d_alpha0;
+
+	/*Handling Matlab output data*/
+	dimX = dim_array[0]; dimY = dim_array[1];
+
+	if (number_of_dims == 2) {
+		/*2D case*/
+		dimZ = 1;
+		bp::tuple shape = bp::make_tuple(dim_array[0], dim_array[1]);
+		np::dtype dtype = np::dtype::get_builtin<float>();
+
+		np::ndarray npU = np::zeros(shape, dtype);
+		np::ndarray npP1 = np::zeros(shape, dtype);
+		np::ndarray npP2 = np::zeros(shape, dtype);
+		np::ndarray npQ1 = np::zeros(shape, dtype);
+		np::ndarray npQ2 = np::zeros(shape, dtype);
+		np::ndarray npQ3 = np::zeros(shape, dtype);
+		np::ndarray npV1 = np::zeros(shape, dtype);
+		np::ndarray npV1_old = np::zeros(shape, dtype);
+		np::ndarray npV2 = np::zeros(shape, dtype);
+		np::ndarray npV2_old = np::zeros(shape, dtype);
+		np::ndarray npU_old = np::zeros(shape, dtype);
+
+		U = reinterpret_cast<float *>(npU.get_data());
+		U_old = reinterpret_cast<float *>(npU_old.get_data());
+		P1 = reinterpret_cast<float *>(npP1.get_data());
+		P2 = reinterpret_cast<float *>(npP2.get_data());
+		Q1 = reinterpret_cast<float *>(npQ1.get_data());
+		Q2 = reinterpret_cast<float *>(npQ2.get_data());
+		Q3 = reinterpret_cast<float *>(npQ3.get_data());
+		V1 = reinterpret_cast<float *>(npV1.get_data());
+		V1_old = reinterpret_cast<float *>(npV1_old.get_data());
+		V2 = reinterpret_cast<float *>(npV2.get_data());
+		V2_old = reinterpret_cast<float *>(npV2_old.get_data());
+		//U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+
+		/*dual variables*/
+		/*P1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		P2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+
+		Q1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		Q2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		Q3 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+
+		U_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+
+		V1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		V1_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		V2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));
+		V2_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL));*/
+		/*printf("%i \n", i);*/
+		L2 = 12.0; /*Lipshitz constant*/
+		tau = 1.0 / pow(L2, 0.5);
+		sigma = 1.0 / pow(L2, 0.5);
+
+		/*Copy A to U*/
+		copyIm(A, U, dimX, dimY, dimZ);
+		/* Here primal-dual iterations begin for 2D */
+		for (ll = 0; ll < iter; ll++) {
+
+			/* Calculate Dual Variable P */
+			DualP_2D(U, V1, V2, P1, P2, dimX, dimY, dimZ, sigma);
+
+			/*Projection onto convex set for P*/
+			ProjP_2D(P1, P2, dimX, dimY, dimZ, alpha1);
+
+			/* Calculate Dual Variable Q */
+			DualQ_2D(V1, V2, Q1, Q2, Q3, dimX, dimY, dimZ, sigma);
+
+			/*Projection onto convex set for Q*/
+			ProjQ_2D(Q1, Q2, Q3, dimX, dimY, dimZ, alpha0);
+
+			/*saving U into U_old*/
+			copyIm(U, U_old, dimX, dimY, dimZ);
+
+			/*adjoint operation  -> divergence and projection of P*/
+			DivProjP_2D(U, A, P1, P2, dimX, dimY, dimZ, lambda, tau);
+
+			/*get updated solution U*/
+			newU(U, U_old, dimX, dimY, dimZ);
+
+			/*saving V into V_old*/
+			copyIm(V1, V1_old, dimX, dimY, dimZ);
+			copyIm(V2, V2_old, dimX, dimY, dimZ);
+
+			/* upd V*/
+			UpdV_2D(V1, V2, P1, P2, Q1, Q2, Q3, dimX, dimY, dimZ, tau);
+
+			/*get new V*/
+			newU(V1, V1_old, dimX, dimY, dimZ);
+			newU(V2, V2_old, dimX, dimY, dimZ);
+		} /*end of iterations*/
+	
+		result.append<np::ndarray>(npU);
+	}
+	
+
+	
+	
+	return result;
+}
+
+BOOST_PYTHON_MODULE(cpu_regularizers)
+{
+	np::initialize();
+
+	//To specify that this module is a package
+	bp::object package = bp::scope();
+	package.attr("__path__") = "cpu_regularizers";
+
+	np::dtype dt1 = np::dtype::get_builtin<uint8_t>();
+	np::dtype dt2 = np::dtype::get_builtin<uint16_t>();
+
+	def("SplitBregman_TV", SplitBregman_TV);
+	def("FGP_TV", FGP_TV);
+	def("LLT_model", LLT_model);
+	def("PatchBased_Regul", PatchBased_Regul);
+	def("TGV_PD", TGV_PD);
+}
diff --git a/Wrappers/Python/src/fista_module_gpu.pyx b/Wrappers/Python/src/fista_module_gpu.pyx
new file mode 100644
index 0000000..9d5b15a
--- /dev/null
+++ b/Wrappers/Python/src/fista_module_gpu.pyx
@@ -0,0 +1,154 @@
+# distutils: language=c++
+"""
+Copyright 2018 CCPi
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Author: Edoardo Pasca
+"""
+
+import cython
+
+import numpy as np
+cimport numpy as np
+
+
+cdef extern void Diff4th_GPU_kernel(float* A, float* B, int N, int M, int Z,
+                            float sigma, int iter, float tau, float lambdaf);
+cdef extern void NLM_GPU_kernel(float *A, float* B, float *Eucl_Vec, 
+                                int N, int M,  int Z, int dimension, 
+                                int SearchW, int SimilW, 
+                                int SearchW_real, float denh2, float lambdaf);
+
+def Diff4thHajiaboli(inputData, 
+                     regularization_parameter, 
+                     iterations, 
+                     edge_preserving_parameter):
+    if inputData.ndims == 2:
+        return Diff4thHajiaboli2D(inputData,  
+                     regularization_parameter, 
+                     iterations, 
+                     edge_preserving_parameter)
+    elif inputData.ndims == 3:
+        return Diff4thHajiaboli3D(inputData,  
+                     regularization_parameter, 
+                     iterations, 
+                     edge_preserving_parameter)
+                        
+def Diff4thHajiaboli2D(np.ndarray[np.float32_t, ndim=2, mode="c"] inputData, 
+                     float regularization_parameter, 
+                     int iterations, 
+                     float edge_preserving_parameter):
+    
+    cdef long dims[2]
+    dims[0] = inputData.shape[0]
+    dims[1] = inputData.shape[1]
+    N = dims[0] + 2;
+    M = dims[1] + 2;
+    
+    #time step is sufficiently small for an explicit methods
+    tau = 0.01
+    
+    #A_L = (float*)mxGetData(mxCreateNumericMatrix(N, M, mxSINGLE_CLASS, mxREAL));
+    #B_L = (float*)mxGetData(mxCreateNumericMatrix(N, M, mxSINGLE_CLASS, mxREAL));
+    A_L = np.zeros((N,M), dtype=np.float)
+    B_L = np.zeros((N,M), dtype=np.float)
+    B = np.zeros((dims[0],dims[1]), dtype=np.float)
+    #A = inputData
+
+    # copy A to the bigger A_L with boundaries
+    #pragma omp parallel for shared(A_L, A) private(i,j)
+    cdef int i, j;
+    for i in range(N):
+        for j in range(M):
+            if (((i > 0) and (i < N-1)) and  ((j > 0) and (j < M-1))):
+                #A_L[i*M+j] = A[(i-1)*(dims[1])+(j-1)]
+                A_L[i][j] = inputData[i-1][j-1]
+        
+    # Running CUDA code here
+    #Diff4th_GPU_kernel(A_L, B_L, N, M, Z, (float)sigma, iter, (float)tau, lambda);    
+#    Diff4th_GPU_kernel(
+#            #<float*> A_L.data, <float*> B_L.data,
+#            &A_L[0,0], &B_L[0,0], 
+#                       N, M, 0, 
+#                       edge_preserving_parameter,
+#                       iterations , 
+#                       tau, 
+#                       regularization_parameter)
+    # copy the processed B_L to a smaller B
+    for i in range(N):
+        for j in range(M):
+            if (((i > 0) and (i < N-1)) and ((j > 0) and (j < M-1))):
+                B[i-1][j-1] = B_L[i][j]
+    ##pragma omp parallel for shared(B_L, B) private(i,j)
+    #for (i=0; i < N; i++) {
+    #    for (j=0; j < M; j++) {
+    #        if (((i > 0) && (i < N-1)) &&  ((j > 0) && (j < M-1)))   B[(i-1)*(dims[1])+(j-1)] = B_L[i*M+j];
+    #    }}
+     
+    return B
+
+def Diff4thHajiaboli3D(np.ndarray[np.float32_t, ndim=3, mode="c"] inputData, 
+                     float regularization_parameter, 
+                     int iterations, 
+                     float edge_preserving_parameter):
+    
+    cdef long dims[3]
+    dims[0] = inputData.shape[0]
+    dims[1] = inputData.shape[1]
+    dims[2] = inputData.shape[2]
+    N = dims[0] + 2
+    M = dims[1] + 2
+    Z = dims[2] + 2
+    
+    # Time Step is small for an explicit methods
+    tau = 0.0007;
+    
+    #A_L = (float*)mxGetData(mxCreateNumericMatrix(N, M, mxSINGLE_CLASS, mxREAL));
+    #B_L = (float*)mxGetData(mxCreateNumericMatrix(N, M, mxSINGLE_CLASS, mxREAL));
+    A_L = np.zeros((N,M,Z), dtype=np.float)
+    B_L = np.zeros((N,M,Z), dtype=np.float)
+    B = np.zeros((dims[0],dims[1],dims[2]), dtype=np.float)
+    #A = inputData
+    
+    # copy A to the bigger A_L with boundaries
+    #pragma omp parallel for shared(A_L, A) private(i,j)
+    cdef int i, j, k;
+    for i in range(N):
+        for j in range(M):
+            for k in range(Z):
+                if (((i > 0) and (i < N-1)) and \
+                    ((j > 0) and (j < M-1)) and \
+                    ((k > 0) and (k < Z-1))):
+                        A_L[i][j][k] = inputData[i-1][j-1][k-1];
+        
+    # Running CUDA code here
+    #Diff4th_GPU_kernel(A_L, B_L, N, M, Z, (float)sigma, iter, (float)tau, lambda);    
+#    Diff4th_GPU_kernel(
+#            #<float*> A_L.data, <float*> B_L.data,
+#            &A_L[0,0,0], &B_L[0,0,0], 
+#                       N, M, Z, 
+#                       edge_preserving_parameter,
+#                       iterations , 
+#                       tau, 
+#                       regularization_parameter)
+    # copy the processed B_L to a smaller B
+    for i in range(N):
+        for j in range(M):
+            for k in range(Z):
+                if (((i > 0) and (i < N-1)) and \
+                    ((j > 0) and (j < M-1)) and \
+                    ((k > 0) and (k < Z-1))):
+                    B[i-1][j-1][k-1] = B_L[i][j][k]
+    
+     
+    return B
+
+		
diff --git a/Wrappers/Python/src/multiply.pyx b/Wrappers/Python/src/multiply.pyx
new file mode 100644
index 0000000..65df1c6
--- /dev/null
+++ b/Wrappers/Python/src/multiply.pyx
@@ -0,0 +1,49 @@
+"""
+multiply.pyx
+
+simple cython test of accessing a numpy array's data
+
+the C function: c_multiply multiplies all the values in a 2-d array by a scalar, in place.
+
+"""
+
+import cython
+
+# import both numpy and the Cython declarations for numpy
+import numpy as np
+cimport numpy as np
+
+# declare the interface to the C code
+cdef extern void c_multiply (double* array, double value, int m, int n)
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def multiply(np.ndarray[double, ndim=2, mode="c"] input not None, double value):
+    """
+    multiply (arr, value)
+
+    Takes a numpy arry as input, and multiplies each elemetn by value, in place
+
+    param: array -- a 2-d numpy array of np.float64
+    param: value -- a number that will be multiplied by each element in the array
+
+    """
+    cdef int m, n
+
+    m, n = input.shape[0], input.shape[1]
+
+    c_multiply (&input[0,0], value, m, n)
+
+    return None
+
+def multiply2(np.ndarray[double, ndim=2, mode="c"] input not None, double value):
+    """
+    this method works fine, but is not as future-proof the nupy API might change, etc.
+    """
+    cdef int m, n
+
+    m, n = input.shape[0], input.shape[1]
+
+    c_multiply (<double*> input.data, value, m, n)
+
+    return None
\ No newline at end of file
-- 
cgit v1.2.3