Spatially variant regularisation field
dkazanc opened this issue · 10 comments
- One requires a capability of passing not only a scalar but a vector of regularisation values.
- When the N-dimensional field of regularisation parameters is evaluated, it can be a very powerful tool to incorporate better models into an algorithm making it robust against data inconsistencies.
Current implementation accepts only scalars for regularisation parameter so the core must be modified.
I've had a quick look at this function.
A simple way without changing the code too much, and avoiding repetition would be to pass a pointer to lambda and a flag on whether it's an array or not. Then keeping your code mostly the same you just need to step through the array if it's an array, and don't step if it's not:
So here lambda_is_arr
equals 1 or 0.
and the value used in your final calculation is then:
lambda_val = *(lambda + index* lambda_is_arr);
It's not a very safe solution - you'll have to make check the size of lambda beforehand.
So an example would be something like this:
float TV_kernel(float *D1, float *D2, float *D3, float *B, float *A, float * lambda, int lambda_is_arr, float tau, long dimX, long dimY, long dimZ)
{
float lambda_val;
float dv1, dv2, dv3;
long index, i, j, k, i1, i2, k1, j1, j2, k2;
if (dimZ > 1)
{
#pragma omp parallel for shared (D1, D2, D3, B, dimX, dimY, dimZ) private(index, i, j, k, i1, j1, k1, i2, j2, k2, dv1,dv2,dv3,lambda_val)
for (j = 0; j < dimY; j++)
{
for (i = 0; i < dimX; i++)
{
for (k = 0; k < dimZ; k++)
{
index = (dimX*dimY)*k + j * dimX + i;
lambda_val = *(lambda + index* lambda_is_arr);
/* symmetric boundary conditions (Neuman) */
i1 = i + 1; if (i1 >= dimX) i1 = i - 1;
i2 = i - 1; if (i2 < 0) i2 = i + 1;
j1 = j + 1; if (j1 >= dimY) j1 = j - 1;
j2 = j - 1; if (j2 < 0) j2 = j + 1;
k1 = k + 1; if (k1 >= dimZ) k1 = k - 1;
k2 = k - 1; if (k2 < 0) k2 = k + 1;
/*divergence components */
dv1 = D1[index] - D1[(dimX*dimY)*k + j2 * dimX + i];
dv2 = D2[index] - D2[(dimX*dimY)*k + j * dimX + i2];
dv3 = D3[index] - D3[(dimX*dimY)*k2 + j * dimX + i];
B[index] += tau * (lambda_val*(dv1 + dv2 + dv3) - (B[index] - A[index]));
}
}
}
}
else
{
#pragma omp parallel for shared (D1, D2, B, dimX, dimY) private(index, i, j, i1, j1, i2, j2,dv1,dv2,lambda_val)
for (j = 0; j < dimY; j++)
{
for (i = 0; i < dimX; i++)
{
index = j * dimX + i;
lambda_val = *(lambda + index * lambda_is_arr);
/* symmetric boundary conditions (Neuman) */
i1 = i + 1; if (i1 >= dimX) i1 = i - 1;
i2 = i - 1; if (i2 < 0) i2 = i + 1;
j1 = j + 1; if (j1 >= dimY) j1 = j - 1;
j2 = j - 1; if (j2 < 0) j2 = j + 1;
/* divergence components */
dv1 = D1[index] - D1[j2*dimX + i];
dv2 = D2[index] - D2[j*dimX + i2];
B[index] += tau * (lambda_val*(dv1 + dv2) - (B[index] - A[index]));
}
}
}
return *B;
}
Thanks a lot @gfardell, I find this as a quite neat solution actually! The problem I'm currently having is how to process inputs in Cython.
E.g. this function/wrapper
def TV_ROF_2D(np.ndarray[np.float32_t, ndim=2, mode="c"] inputData,
np.ndarray[np.float32_t, ndim=2, mode="c"] regularisation_parameter,
int iterationsNumb,
float marching_step_parameter,
float tolerance_param):
So the regularisation_parameter
is defined and fixed as a 2D array, i.e. I cannot pass a scalar to it. Is there any other way around it? cheers
Actually I think I know what I should do. I need to check what regularisation_parameter
is before calling the function.
def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param):
if inputData.ndim == 2:
#### check regularisation_parameter
return TV_ROF_2D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
probably not very neat but this is what I do:
def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param):
if inputData.ndim == 2:
if regularisation_parameter.ndim == 2:
return TV_ROF_2D_vecreg(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
else:
return TV_ROF_2D_scalreg(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
Then there are 2 separate functions dealing with scalar or vector regularisation_parameter
I suppose you are looking at this
I'd probably change it to something like this
def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param):
cdef float lambdareg;
if inputData.ndim == 2:
if isinstance (regularisation_parameter, np.ndarray):
return TV_ROF_2D(inputData, ®ularisation_parameter[0,0], 1, iterationsNumb, marching_step_parameter,tolerance_param)
else: # supposedly this would be a float
lambdareg = regularisation_parameter
return TV_ROF_2D(inputData, &lambdareg, 0, iterationsNumb, marching_step_parameter,tolerance_param)
elif inputData.ndim == 3:
return TV_ROF_3D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
def TV_ROF_2D(np.ndarray[np.float32_t, ndim=2, mode="c"] inputData,
float * regularisation_parameter, int is_array,
int iterationsNumb,
float marching_step_parameter,
float tolerance_param):
cdef long dims[2]
dims[0] = inputData.shape[0]
dims[1] = inputData.shape[1]
cdef np.ndarray[np.float32_t, ndim=2, mode="c"] outputData = \
np.zeros([dims[0],dims[1]], dtype='float32')
cdef np.ndarray[np.float32_t, ndim=1, mode="c"] infovec = \
np.ones([2], dtype='float32')
# Run ROF iterations for 2D data
TV_ROF_CPU_main(&inputData[0,0], &outputData[0,0], &infovec[0], regularisation_parameter, is_array, iterationsNumb, marching_step_parameter, tolerance_param, dims[1], dims[0], 1)
return (outputData,infovec)
EDITED
Thanks. I've got 2 errors:
return TV_ROF_2D(inputData, ®ularisation_parameter[0,0], 1, iterationsNumb, marching_step_parameter,tolerance_param)
src/cpu_regularisers.pyx:45:40: Cannot take address of Python object```
and
return TV_ROF_2D(inputData, &lambdareg, 0, iterationsNumb, marching_step_parameter,tolerance_param)
src/cpu_regularisers.pyx:48:40: Cannot convert 'float *' to Python object
both errors are about the second parameter in TV_ROF_2D
I suppose that's right. We need to do that check in the python function and pass the C function what it expects. Try this
def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param):
if inputData.ndim == 2:
return TV_ROF_2D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
elif inputData.ndim == 3:
return TV_ROF_3D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
def TV_ROF_2D(np.ndarray[np.float32_t, ndim=2, mode="c"] inputData,
regularisation_parameter,
int iterationsNumb,
float marching_step_parameter,
float tolerance_param):
cdef long dims[2]
dims[0] = inputData.shape[0]
dims[1] = inputData.shape[1]
cdef float lambdareg;
cdef np.ndarray[np.float32_t, ndim=2, mode="c"] outputData = \
np.zeros([dims[0],dims[1]], dtype='float32')
cdef np.ndarray[np.float32_t, ndim=1, mode="c"] infovec = \
np.ones([2], dtype='float32')
# Run ROF iterations for 2D data
if isinstance (regularisation_parameter, np.ndarray):
TV_ROF_CPU_main(&inputData[0,0], &outputData[0,0], &infovec[0], ®ularisation_parameter[0,0], 1, iterationsNumb, marching_step_parameter, tolerance_param, dims[1], dims[0], 1)
else: # supposedly this would be a float
lambdareg = regularisation_parameter;
TV_ROF_CPU_main(&inputData[0,0], &outputData[0,0], &infovec[0], &lambdareg, 0, iterationsNumb, marching_step_parameter, tolerance_param, dims[1], dims[0], 1)
return (outputData,infovec)
thanks, we still get the Cannot take address of Python object
error for ®ularisation_parameter[0,0]
OK, the reason is, I presume, that regularisation_parameter
is a Python object, whatever that may be.
I think one solution is to copy the regularisation_parameter
array to a cdef
array.
I haven't tested this
def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param):
if inputData.ndim == 2:
return TV_ROF_2D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
elif inputData.ndim == 3:
return TV_ROF_3D(inputData, regularisation_parameter, iterationsNumb, marching_step_parameter,tolerance_param)
def TV_ROF_2D(np.ndarray[np.float32_t, ndim=2, mode="c"] inputData,
regularisation_parameter,
int iterationsNumb,
float marching_step_parameter,
float tolerance_param):
cdef long dims[2]
dims[0] = inputData.shape[0]
dims[1] = inputData.shape[1]
cdef float lambdareg;
cdef np.ndarray[np.float32_t, ndim=2, mode="c"] reg;
cdef np.ndarray[np.float32_t, ndim=2, mode="c"] outputData = \
np.zeros([dims[0],dims[1]], dtype='float32')
cdef np.ndarray[np.float32_t, ndim=1, mode="c"] infovec = \
np.ones([2], dtype='float32')
# Run ROF iterations for 2D data
if isinstance (regularisation_parameter, np.ndarray):
reg = regularisation_parameter.copy()
TV_ROF_CPU_main(&inputData[0,0], &outputData[0,0], &infovec[0], ®[0,0], 1, iterationsNumb, marching_step_parameter, tolerance_param, dims[1], dims[0], 1)
else: # supposedly this would be a float
lambdareg = regularisation_parameter;
TV_ROF_CPU_main(&inputData[0,0], &outputData[0,0], &infovec[0], &lambdareg, 0, iterationsNumb, marching_step_parameter, tolerance_param, dims[1], dims[0], 1)
return (outputData,infovec)