Commit 9bf7ec1c authored by Martin Uecker's avatar Martin Uecker

model-based reconstruction for look-locker experiments

parent f7671445
...@@ -169,6 +169,7 @@ MODULES_pics = -lgrecon -lsense -liter -llinops -lwavelet -llowrank -lnoncart ...@@ -169,6 +169,7 @@ MODULES_pics = -lgrecon -lsense -liter -llinops -lwavelet -llowrank -lnoncart
MODULES_sqpics = -lsense -liter -llinops -lwavelet -llowrank -lnoncart MODULES_sqpics = -lsense -liter -llinops -lwavelet -llowrank -lnoncart
MODULES_pocsense = -lsense -liter -llinops -lwavelet MODULES_pocsense = -lsense -liter -llinops -lwavelet
MODULES_nlinv = -lnoir -liter -lnlops -llinops MODULES_nlinv = -lnoir -liter -lnlops -llinops
MODULES_moba = -lmoba -lnoir -liter -lnlops -llinops -lwavelet
MODULES_bpsense = -lsense -lnoncart -liter -llinops -lwavelet MODULES_bpsense = -lsense -lnoncart -liter -llinops -lwavelet
MODULES_itsense = -liter -llinops MODULES_itsense = -liter -llinops
MODULES_ecalib = -lcalib MODULES_ecalib = -lcalib
...@@ -184,7 +185,7 @@ MODULES_rof = -liter -llinops ...@@ -184,7 +185,7 @@ MODULES_rof = -liter -llinops
MODULES_tgv = -liter -llinops MODULES_tgv = -liter -llinops
MODULES_bench = -lwavelet -llinops MODULES_bench = -lwavelet -llinops
MODULES_phantom = -lsimu MODULES_phantom = -lsimu
MODULES_bart = -lbox -lgrecon -lsense -lnoir -liter -llinops -lwavelet -llowrank -lnoncart -lcalib -lsimu -lsake -ldfwavelet -lnlops MODULES_bart = -lbox -lgrecon -lsense -lnoir -liter -llinops -lwavelet -llowrank -lnoncart -lcalib -lsimu -lsake -ldfwavelet -lnlops -lmoba
MODULES_sake = -lsake MODULES_sake = -lsake
MODULES_traj = -lnoncart MODULES_traj = -lnoncart
MODULES_wave = -liter -lwavelet -llinops -llowrank MODULES_wave = -liter -lwavelet -llinops -llowrank
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
TBASE=show slice crop resize join transpose squeeze flatten zeros ones flip circshift extract repmat bitmask reshape version delta copy casorati vec poly index TBASE=show slice crop resize join transpose squeeze flatten zeros ones flip circshift extract repmat bitmask reshape version delta copy casorati vec poly index
TFLP=scale invert conj fmac saxpy sdot spow cpyphs creal carg normalize cdf97 pattern nrmse mip avg cabs zexp TFLP=scale invert conj fmac saxpy sdot spow cpyphs creal carg normalize cdf97 pattern nrmse mip avg cabs zexp
TNUM=fft fftmod fftshift noise bench threshold conv rss filter mandelbrot wavelet window var std fftrot TNUM=fft fftmod fftshift noise bench threshold conv rss filter mandelbrot wavelet window var std fftrot
TRECO=pics pocsense sqpics itsense nlinv nufft rof tgv sake wave lrmatrix estdims estshift estdelay wavepsf wshfl TRECO=pics pocsense sqpics itsense nlinv moba nufft rof tgv sake wave lrmatrix estdims estshift estdelay wavepsf wshfl
TCALIB=ecalib ecaltwo caldir walsh cc ccapply calmat svd estvar whiten TCALIB=ecalib ecaltwo caldir walsh cc ccapply calmat svd estvar whiten
TMRI=homodyne poisson twixread fakeksp TMRI=homodyne poisson twixread fakeksp
TSIM=phantom traj TSIM=phantom traj
......
mobasrcs := $(wildcard $(srcdir)/moba/*.c)
mobaobjs := $(mobasrcs:.c=.o)
.INTERMEDIATE: $(mobaobjs)
lib/libmoba.a: libmoba.a($(mobaobjs))
UTARGETS += test_moba
MODULES_test_moba += -lmoba -lnlops -llinops
/* Copyright 2013. The Regents of the University of California.
* Copyright 2019. Uecker Lab, University Medical Center Goettingen.
* All rights reserved. Use of this source code is governed by
* a BSD-style license which can be found in the LICENSE file.
*
* Authors: Xiaoqing Wang, Martin Uecker
*/
#include <stdbool.h>
#include <complex.h>
#include <math.h>
#include "num/multind.h"
#include "num/flpmath.h"
#include "num/fft.h"
#include "num/init.h"
#include "misc/mri.h"
#include "misc/misc.h"
#include "misc/mmio.h"
#include "misc/utils.h"
#include "misc/opts.h"
#include "misc/debug.h"
#include "noir/recon.h"
#include "moba/recon_T1.h"
static const char usage_str[] = "<kspace> <TI/TE> <output> [<sensitivities>]";
static const char help_str[] = "Model-based nonlinear inverse reconstruction\n";
int main_moba(int argc, char* argv[])
{
double start_time = timestamp();
float restrict_fov = -1.;
const char* psf = NULL;
struct noir_conf_s conf = noir_defaults;
bool out_sens = false;
bool usegpu = false;
bool unused = false;
enum mdb_t { MDB_T1 } mode = { MDB_T1 };
const struct opt_s opts[] = {
OPT_SELECT('L', enum mdb_t, &mode, MDB_T1, "T1 mapping using model-based look-locker"),
OPT_UINT('i', &conf.iter, "iter", "Number of Newton steps"),
OPT_FLOAT('R', &conf.redu, "", "(reduction factor)"),
OPT_FLOAT('j', &conf.alpha_min, "", "Minimum regu. parameter"),
OPT_INT('d', &debug_level, "level", "Debug level"),
OPT_SET('N', &unused, "(normalize)"), // no-op
OPT_FLOAT('f', &restrict_fov, "FOV", ""),
OPT_STRING('p', &psf, "PSF", ""),
OPT_SET('g', &usegpu, "use gpu"),
};
cmdline(&argc, argv, 2, 4, usage_str, help_str, ARRAY_SIZE(opts), opts);
if (5 == argc)
out_sens = true;
num_init();
long ksp_dims[DIMS];
complex float* kspace_data = load_cfl(argv[1], DIMS, ksp_dims);
long TI_dims[DIMS];
complex float* TI = load_cfl(argv[2], DIMS, TI_dims);
assert(TI_dims[TE_DIM] == ksp_dims[TE_DIM]);
assert(1 == ksp_dims[MAPS_DIM]);
long dims[DIMS];
md_copy_dims(DIMS, dims, ksp_dims);
long img_dims[DIMS];
md_select_dims(DIMS, FFT_FLAGS|MAPS_FLAG|COEFF_FLAG|SLICE_FLAG|TIME2_FLAG, img_dims, dims);
img_dims[COEFF_DIM] = 3;
long img_strs[DIMS];
md_calc_strides(DIMS, img_strs, img_dims, CFL_SIZE);
long single_map_dims[DIMS];
md_select_dims(DIMS, FFT_FLAGS|MAPS_FLAG|SLICE_FLAG|TIME2_FLAG, single_map_dims, dims);
long single_map_strs[DIMS];
md_calc_strides(DIMS, single_map_strs, single_map_dims, CFL_SIZE);
long coil_dims[DIMS];
md_select_dims(DIMS, FFT_FLAGS|COIL_FLAG|MAPS_FLAG|SLICE_FLAG|TIME2_FLAG, coil_dims, dims);
long coil_strs[DIMS];
md_calc_strides(DIMS, coil_strs, coil_dims, CFL_SIZE);
complex float* img = create_cfl(argv[3], DIMS, img_dims);
complex float* single_map = create_cfl("", DIMS, single_map_dims);
long msk_dims[DIMS];
md_select_dims(DIMS, FFT_FLAGS, msk_dims, dims);
long msk_strs[DIMS];
md_calc_strides(DIMS, msk_strs, msk_dims, CFL_SIZE);
complex float* mask = NULL;
complex float* norm = md_alloc(DIMS, img_dims, CFL_SIZE);
complex float* sens = (out_sens ? create_cfl : anon_cfl)(out_sens ? argv[4] : "", DIMS, coil_dims);
md_zfill(DIMS, img_dims, img, 1.0);
md_clear(DIMS, coil_dims, sens, CFL_SIZE);
complex float* pattern = NULL;
long pat_dims[DIMS];
if (NULL != psf) {
complex float* tmp_psf =load_cfl(psf, DIMS, pat_dims);
pattern = anon_cfl("", DIMS, pat_dims);
md_copy(DIMS, pat_dims, pattern, tmp_psf, CFL_SIZE);
unmap_cfl(DIMS, pat_dims, tmp_psf);
// FIXME: check compatibility
if (-1 == restrict_fov)
restrict_fov = 0.5;
conf.noncart = true;
} else {
md_copy_dims(DIMS, pat_dims, img_dims);
pattern = anon_cfl("", DIMS, pat_dims);
estimate_pattern(DIMS, ksp_dims, COIL_FLAG, pattern, kspace_data);
}
double scaling = 5000. / md_znorm(DIMS, ksp_dims, kspace_data);
double scaling_psf = 1000. / md_znorm(DIMS, pat_dims, pattern);
debug_printf(DP_INFO, "Scaling: %f\n", scaling);
md_zsmul(DIMS, ksp_dims, kspace_data, kspace_data, scaling);
debug_printf(DP_INFO, "Scaling_psf: %f\n", scaling_psf);
md_zsmul(DIMS, pat_dims, pattern, pattern, scaling_psf);
if (-1. == restrict_fov) {
mask = md_alloc(DIMS, msk_dims, CFL_SIZE);
md_zfill(DIMS, msk_dims, mask, 1.);
} else {
float restrict_dims[DIMS] = { [0 ... DIMS - 1] = 1. };
restrict_dims[0] = restrict_fov;
restrict_dims[1] = restrict_fov;
restrict_dims[2] = restrict_fov;
mask = compute_mask(DIMS, msk_dims, restrict_dims);
//md_zsmul2(DIMS, img_dims, img_strs, img, msk_strs, mask ,1.0);
md_zmul2(DIMS, img_dims, img_strs, img, img_strs, img, msk_strs, mask);
// Choose a different initial guess for R1*
long pos[DIMS];
for (int i = 0; i < (int)DIMS; i++)
pos[i] = 0;
pos[COEFF_DIM] = 2;
md_copy_block(DIMS, pos, single_map_dims, single_map, img_dims, img, CFL_SIZE);
md_zsmul2(DIMS, single_map_dims, single_map_strs, single_map, single_map_strs, single_map, 1.5);
md_copy_block(DIMS, pos, img_dims, img, single_map_dims, single_map, CFL_SIZE);
}
// conf.alpha = 0.1;
#ifdef USE_CUDA
if (usegpu) {
complex float* kspace_gpu = md_alloc_gpu(DIMS, ksp_dims, CFL_SIZE);
md_copy(DIMS, ksp_dims, kspace_gpu, kspace_data, CFL_SIZE);
complex float* TI_gpu = md_alloc_gpu(DIMS, TI_dims, CFL_SIZE);
md_copy(DIMS, TI_dims, TI_gpu, TI, CFL_SIZE);
switch (mode) {
case MDB_T1:
T1_recon(&conf, dims, img, sens, pattern, mask, TI_gpu, kspace_gpu, usegpu);
break;
};
md_free(kspace_gpu);
md_free(TI_gpu);
} else
#endif
switch (mode) {
case MDB_T1:
T1_recon(&conf, dims, img, sens, pattern, mask, TI, kspace_data, usegpu);
break;
};
md_free(norm);
md_free(mask);
unmap_cfl(DIMS, coil_dims, sens);
unmap_cfl(DIMS, pat_dims, pattern);
unmap_cfl(DIMS, img_dims, img);
unmap_cfl(DIMS, single_map_dims, single_map);
unmap_cfl(DIMS, ksp_dims, kspace_data);
unmap_cfl(DIMS, TI_dims, TI);
double recosecs = timestamp() - start_time;
debug_printf(DP_DEBUG2, "Total Time: %.2f s\n", recosecs);
exit(0);
}
This diff is collapsed.
struct nlop_s;
struct noir_model_conf_s;
extern struct nlop_s* nlop_T1_create(int N, const long map_dims[N], const long out_dims[N], const long in_dims[N],
const long TI_dims[N], const complex float* TI, bool use_gpu);
/* Copyright 2013-2014. The Regents of the University of California.
* Copyright 2019. Uecker Lab, University Medical Center Goettingen.
* All rights reserved. Use of this source code is governed by
* a BSD-style license which can be found in the LICENSE file.
*
* Authors: Xiaoqing Wang, Martin Uecker
*/
#include <assert.h>
#include <stdbool.h>
#include <math.h>
#include <stdio.h>
#include "misc/types.h"
#include "misc/mri.h"
#include "misc/debug.h"
#include "misc/misc.h"
#include "num/multind.h"
#include "num/flpmath.h"
#include "num/ops_p.h"
#include "num/rand.h"
#include "num/ops.h"
#include "num/iovec.h"
#include "wavelet/wavthresh.h"
#include "nlops/nlop.h"
#include "iter/prox.h"
#include "iter/vec.h"
#include "iter/italgos.h"
#include "iter/iter3.h"
#include "iter/iter2.h"
#include "iter_l1.h"
struct T1inv_s {
INTERFACE(iter_op_data);
const struct nlop_s* nlop;
const struct iter3_irgnm_conf* conf;
long size_x;
long size_y;
float alpha;
const long* dims;
bool first_iter;
int outer_iter;
const struct operator_p_s* prox1;
const struct operator_p_s* prox2;
};
DEF_TYPEID(T1inv_s);
static void normal_fista(iter_op_data* _data, float* dst, const float* src)
{
auto data = CAST_DOWN(T1inv_s, _data);
float* tmp = md_alloc_sameplace(1, MD_DIMS(data->size_y), FL_SIZE, src);
linop_forward_unchecked(nlop_get_derivative(data->nlop, 0, 0), (complex float*)tmp, (const complex float*)src);
linop_adjoint_unchecked(nlop_get_derivative(data->nlop, 0, 0), (complex float*)dst, (const complex float*)tmp);
md_free(tmp);
long res = data->dims[0];
long parameters = data->dims[COEFF_DIM];
long coils = data->dims[COIL_DIM];
md_axpy(1, MD_DIMS(data->size_x * coils / (coils + parameters)),
dst + res * res * 2 * parameters,
data->alpha,
src + res * res * 2 * parameters);
}
static void pos_value(iter_op_data* _data, float* dst, const float* src)
{
auto data = CAST_DOWN(T1inv_s, _data);
long res = data->dims[0];
long parameters = data->dims[COEFF_DIM];
long dims1[DIMS];
md_select_dims(DIMS, FFT_FLAGS, dims1, data->dims);
md_zsmax(DIMS, dims1, (_Complex float*)dst + (parameters - 1) * res * res,
(const _Complex float*)src + (parameters - 1) * res * res, 0.1);
}
static void combined_prox(iter_op_data* _data, float rho, float* dst, const float* src)
{
struct T1inv_s* data = CAST_DOWN(T1inv_s, _data);
if (data->first_iter) {
data->first_iter = false;
} else {
pos_value(_data, dst, src);
}
operator_p_apply_unchecked(data->prox2, rho, (_Complex float*)dst, (const _Complex float*)dst);
pos_value(_data, dst, dst);
}
static void inverse_fista(iter_op_data* _data, float alpha, float* dst, const float* src)
{
auto data = CAST_DOWN(T1inv_s, _data);
data->alpha = alpha; // update alpha for normal operator
void* x = md_alloc_sameplace(1, MD_DIMS(data->size_x), FL_SIZE, src);
md_gaussian_rand(1, MD_DIMS(data->size_x / 2), x);
double maxeigen = power(20, data->size_x, select_vecops(src), (struct iter_op_s){ normal_fista, CAST_UP(data) }, x);
md_free(x);
double step = data->conf->step / maxeigen;
debug_printf(DP_DEBUG3, "##reg. alpha = %f\n", alpha);
wavthresh_rand_state_set(data->prox1, 1);
int maxiter = MIN(data->conf->cgiter, 10 * powf(2, data->outer_iter));
float* tmp = md_alloc_sameplace(1, MD_DIMS(data->size_y), FL_SIZE, src);
linop_adjoint_unchecked(nlop_get_derivative(data->nlop, 0, 0), (complex float*)tmp, (const complex float*)src);
float eps = md_norm(1, MD_DIMS(data->size_x), tmp);
data->first_iter = true;
NESTED(void, continuation, (struct ist_data* itrdata))
{
itrdata->scale = data->alpha;
};
fista(maxiter, data->conf->cgtol * alpha * eps, step,
data->size_x,
select_vecops(src),
continuation,
(struct iter_op_s){ normal_fista, CAST_UP(data) },
(struct iter_op_p_s){ combined_prox, CAST_UP(data) },
dst, tmp, NULL);
pos_value(CAST_UP(data), dst, dst);
md_free(tmp);
data->outer_iter++;
}
static const struct operator_p_s* create_prox(const long img_dims[DIMS])
{
bool randshift = true;
long minsize[DIMS] = { [0 ... DIMS - 1] = 1 };
unsigned int wflags = 0;
for (unsigned int i = 0; i < DIMS; i++) {
if ((1 < img_dims[i]) && MD_IS_SET(FFT_FLAGS, i)) {
wflags = MD_SET(wflags, i);
minsize[i] = MIN(img_dims[i], DIMS);
}
}
return prox_wavelet_thresh_create(DIMS, img_dims, wflags, COEFF_FLAG, minsize, 1., randshift);
}
struct T1inv2_s {
INTERFACE(operator_data_t);
struct T1inv_s data;
};
DEF_TYPEID(T1inv2_s);
static void T1inv_apply(const operator_data_t* _data, float alpha, complex float* dst, const complex float* src)
{
const auto data = &CAST_DOWN(T1inv2_s, _data)->data;
inverse_fista(CAST_UP(data), alpha, (float*)dst, (const float*)src);
}
static void T1inv_del(const operator_data_t* _data)
{
auto data = CAST_DOWN(T1inv2_s, _data);
operator_p_free(data->data.prox1);
operator_p_free(data->data.prox2);
nlop_free(data->data.nlop);
xfree(data->data.dims);
xfree(data);
}
static const struct operator_p_s* T1inv_p_create(const struct iter3_irgnm_conf* conf, const long dims[DIMS], struct nlop_s* nlop)
{
PTR_ALLOC(struct T1inv2_s, data);
SET_TYPEID(T1inv2_s, data);
SET_TYPEID(T1inv_s, &data->data);
auto cd = nlop_codomain(nlop);
auto dm = nlop_domain(nlop);
int M = 2 * md_calc_size(cd->N, cd->dims);
int N = 2 * md_calc_size(dm->N, dm->dims);
long* ndims = *TYPE_ALLOC(long[DIMS]);
md_copy_dims(DIMS, ndims, dims);
long img_dims[DIMS];
md_select_dims(DIMS, ~COIL_FLAG, img_dims, dims);
debug_print_dims(DP_INFO, DIMS, img_dims);
auto prox1 = create_prox(img_dims);
auto prox2 = op_p_auto_normalize(prox1, ~COEFF_FLAG);
struct T1inv_s idata = {
{ &TYPEID(T1inv_s) }, nlop_clone(nlop), conf,
N, M, 1.0, ndims, true, 0, prox1, prox2
};
data->data = idata;
return operator_p_create(dm->N, dm->dims, cd->N, cd->dims, CAST_UP(PTR_PASS(data)), T1inv_apply, T1inv_del);
}
struct iterT1_nlop_s {
INTERFACE(iter_op_data);
struct nlop_s nlop;
};
DEF_TYPEID(iterT1_nlop_s);
static void nlop_for_iter(iter_op_data* _o, float* _dst, const float* _src)
{
const auto nlop = CAST_DOWN(iterT1_nlop_s, _o);
operator_apply_unchecked(nlop->nlop.op, (complex float*)_dst, (const complex float*)_src);
}
static void nlop_der_iter(iter_op_data* _o, float* _dst, const float* _src)
{
const auto nlop = CAST_DOWN(iterT1_nlop_s, _o);
assert(2 == operator_nr_args(nlop->nlop.op));
linop_forward_unchecked(nlop->nlop.derivative[0], (complex float*)_dst, (const complex float*)_src);
}
void mdb_irgnm_l1(const iter3_conf* _conf,
const long dims[],
struct nlop_s* nlop,
long N, float* dst,
long M, const float* src)
{
auto cd = nlop_codomain(nlop);
auto dm = nlop_domain(nlop);
assert(M * sizeof(float) == md_calc_size(cd->N, cd->dims) * cd->size);
assert(N * sizeof(float) == md_calc_size(dm->N, dm->dims) * dm->size);
struct iter3_irgnm_conf* conf = CAST_DOWN(iter3_irgnm_conf, _conf);
struct iterT1_nlop_s nl_data = { { &TYPEID(iterT1_nlop_s) }, *nlop };
const struct operator_p_s* inv_op = T1inv_p_create(conf, dims, nlop);
irgnm2(conf->iter, conf->alpha, 0., conf->alpha_min, conf->redu, N, M, select_vecops(src),
(struct iter_op_s){ nlop_for_iter, CAST_UP(&nl_data) },
(struct iter_op_s){ nlop_der_iter, CAST_UP(&nl_data) },
OPERATOR_P2ITOP(inv_op),
dst, NULL, src,
(struct iter_op_s){ NULL, NULL },
NULL);
operator_p_free(inv_op);
}
struct iter3_conf_s;
struct nlop_s;
#ifndef DIMS
#define DIMS 16
#endif
void mdb_irgnm_l1(const struct iter3_conf_s* _conf,
const long dims[DIMS],
struct nlop_s* nlop,
long N, float* dst,
long M, const float* src);
/* Copyright 2019. Uecker Lab, University Medical Center Goettingen.
* All rights reserved. Use of this source code is governed by
* a BSD-style license which can be found in the LICENSE file.
*
* Authors: Xiaoqing Wang, Martin Uecker
*/
#include <stdlib.h>
#include <complex.h>
#include <math.h>
#include <stdbool.h>
#include <assert.h>
#include "misc/misc.h"
#include "misc/mri.h"
#include "misc/mmio.h"
#include "misc/debug.h"
#include "nlops/nlop.h"
#include "nlops/chain.h"
#include "nlops/cast.h"
#include "num/multind.h"
#include "num/flpmath.h"
#include "num/iovec.h"
#include "noir/model.h"
#include "moba/T1fun.h"
#include "model_T1.h"
struct T1_s T1_create(const long dims[DIMS], const complex float* mask, const complex float* TI, const complex float* psf, const struct noir_model_conf_s* conf, _Bool use_gpu)
{
struct noir_s nlinv = noir_create3(dims, mask, psf, conf);
struct T1_s ret;
long map_dims[DIMS];
long out_dims[DIMS];
long in_dims[DIMS];
long TI_dims[DIMS];
md_select_dims(DIMS, conf->fft_flags|TIME2_FLAG, map_dims, dims);
md_select_dims(DIMS, conf->fft_flags|TE_FLAG|TIME2_FLAG, out_dims, dims);
md_select_dims(DIMS, conf->fft_flags|COEFF_FLAG|TIME2_FLAG, in_dims, dims);
md_select_dims(DIMS, TE_FLAG|TIME2_FLAG, TI_dims, dims);
in_dims[COEFF_DIM] = 3;
#if 1
// chain T1 model
struct nlop_s* T1 = nlop_T1_create(DIMS, map_dims, out_dims, in_dims, TI_dims, TI, use_gpu);
debug_print_dims(DP_INFO, DIMS, nlop_generic_domain(T1, 0)->dims);
debug_print_dims(DP_INFO, DIMS, nlop_generic_codomain(T1, 0)->dims);
debug_print_dims(DP_INFO, DIMS, nlop_generic_domain(nlinv.nlop, 0)->dims);
debug_print_dims(DP_INFO, DIMS, nlop_generic_domain(nlinv.nlop, 1)->dims);
debug_print_dims(DP_INFO, DIMS, nlop_generic_codomain(nlinv.nlop, 0)->dims);
const struct nlop_s* b = nlinv.nlop;
const struct nlop_s* c = nlop_chain2(T1, 0, b, 0);
nlop_free(b);
nlinv.nlop = nlop_permute_inputs(c, 2, (const int[2]){ 1, 0 });
nlop_free(c);
#endif
ret.nlop = nlop_flatten(nlinv.nlop);
ret.linop = nlinv.linop;
nlop_free(nlinv.nlop);
return ret;
}
#include "misc/mri.h"
struct linop_s;
struct nlop_s;
struct noir_model_conf_s;
struct T1_s {
struct nlop_s* nlop;
const struct linop_s* linop;
};
extern struct T1_s T1_create(const long dims[DIMS], const complex float* mask, const complex float* TI, const complex float* psf, const struct noir_model_conf_s* conf, _Bool use_gpu);
/* Copyright 2013. The Regents of the University of California.
* Copyright 2019. Uecker Lab, University Medical Center Goettingen.
* All rights reserved. Use of this source code is governed by
* a BSD-style license which can be found in the LICENSE file.
*
* Authors: Xiaoqing Wang, Martin Uecker
*/
#include <complex.h>
#include <stdbool.h>
#include <math.h>
#include <assert.h>
#include <stdio.h>
#include "num/multind.h"
#include "num/flpmath.h"
#include "num/fft.h"
#include "iter/iter3.h"
#include "nlops/nlop.h"
#include "misc/misc.h"
#include "misc/types.h"
#include "misc/mri.h"
#include "misc/debug.h"
#include "noir/model.h"