23#define MAX_DIR_CHANGE 0.2
24#define ANGLE_TOLERANCE 1e-4
48 return (lmax+1) * (lmax+2) /2;
52 inline size_t index (
int l,
int m)
54 return l * (l+1) /2 + m;
60 return (lmax/2+1) * (lmax/2+1);
72 return N ? 2 * std::floor<size_t> ( (std::sqrt (
float (1+8*N))-3.0) /4.0) : 0;
81 template <
class MatrixType>
82 Eigen::Matrix<typename MatrixType::Scalar,Eigen::Dynamic, Eigen::Dynamic>
init_transform (
const MatrixType& dirs,
const int lmax)
84 using namespace Eigen;
85 using value_type =
typename MatrixType::Scalar;
87 throw Exception (
"direction matrix should have 2 columns: [ azimuth elevation ]");
88 Matrix<value_type,Dynamic,Dynamic> SHT (dirs.rows(),
NforL (lmax));
89 Matrix<value_type,Dynamic,1,0,64> AL (lmax+1);
90 for (ssize_t i = 0; i < dirs.rows(); i++) {
93 for (
int l = 0; l <= lmax; l+=2)
94 SHT (i,
index (l,0)) = AL[l];
95 for (
int m = 1; m <= lmax; m++) {
97 for (
int l = ( (m&1) ? m+1 : m); l <= lmax; l+=2) {
110 template <
class MatrixType>
111 Eigen::Matrix<typename MatrixType::Scalar,Eigen::Dynamic, Eigen::Dynamic>
init_transform_cart (
const MatrixType& dirs,
const int lmax)
113 using namespace Eigen;
114 using value_type =
typename MatrixType::Scalar;
115 if (dirs.cols() != 3)
116 throw Exception (
"direction matrix should have 3 columns: [ x y z ]");
117 Matrix<value_type,Dynamic,Dynamic> SHT (dirs.rows(),
NforL (lmax));
118 Matrix<value_type,Dynamic,1,0,64> AL (lmax+1);
119 for (ssize_t i = 0; i < dirs.rows(); i++) {
121 value_type rxy = std::hypot(dirs(i,0), dirs(i,1));
125 for (
int l = 0; l <= lmax; l+=2)
126 SHT (i,
index (l,0)) = AL[l];
128 for (
int m = 1; m <= lmax; m++) {
132 for (
int l = ( (m&1) ? m+1 : m); l <= lmax; l+=2) {
146 template <
class MatrixType,
class VectorType>
149 ssize_t l = 0, nl = 1;
150 for (ssize_t col = 0; col < SH2amp_mapping.cols(); ++col) {
155 SH2amp_mapping.col(col) *= coefs[l];
162 template <
typename MatrixType,
class VectorType>
165 ssize_t l = 0, nl = 1;
166 for (ssize_t row = 0; row < amp2SH_mapping.rows(); ++row) {
171 amp2SH_mapping.row(row) *= coefs[l];
179 template <
typename VectorType>
180 inline Eigen::Matrix<typename VectorType::Scalar,Eigen::Dynamic,1>
invert (
const VectorType& coefs)
182 Eigen::Matrix<typename VectorType::Scalar,Eigen::Dynamic,1> ret (coefs.size());
183 for (
size_t n = 0; n < coefs.size(); ++n)
184 ret[n] = ( coefs[n] ? 1.0 / coefs[n] : 0.0 );
188 template <
typename ValueType>
191 using matrix_type = Eigen::Matrix<ValueType,Eigen::Dynamic,Eigen::Dynamic>;
193 template <
class MatrixType>
194 Transform (
const MatrixType& dirs,
int lmax) :
198 template <
class VectorType>
199 void set_filter (
const VectorType& filter) {
203 template <
class VectorType1,
class VectorType2>
204 void A2SH (VectorType1& sh,
const VectorType2& amplitudes)
const {
205 sh.noalias() =
iSHT * amplitudes;
207 template <
class VectorType1,
class VectorType2>
208 void SH2A (VectorType1& amplitudes,
const VectorType2& sh)
const {
209 amplitudes.noalias() =
SHT * sh;
212 size_t n_SH ()
const {
215 size_t n_amp ()
const {
232 template <
class VectorType>
233 inline typename VectorType::Scalar
value (
const VectorType& coefs,
234 typename VectorType::Scalar cos_elevation,
235 typename VectorType::Scalar cos_azimuth,
236 typename VectorType::Scalar sin_azimuth,
239 using value_type =
typename VectorType::Scalar;
241 Eigen::Matrix<value_type,Eigen::Dynamic,1,0,64> AL (lmax+1);
243 for (
int l = 0; l <= lmax; l+=2)
244 amplitude += AL[l] * coefs[
index (l,0)];
246 for (
int m = 1; m <= lmax; m++) {
248 value_type c = c0 * cos_azimuth - s0 * sin_azimuth;
249 value_type s = s0 * cos_azimuth + c0 * sin_azimuth;
250 for (
int l = ( (m&1) ? m+1 : m); l <= lmax; l+=2)
258 template <
class VectorType>
259 inline typename VectorType::Scalar
value (
const VectorType& coefs,
260 typename VectorType::Scalar cos_elevation,
261 typename VectorType::Scalar azimuth,
264 return value (coefs, cos_elevation, std::cos(azimuth), std::sin(azimuth), lmax);
267 template <
class VectorType1,
class VectorType2>
268 inline typename VectorType1::Scalar
value (
const VectorType1& coefs,
const VectorType2& unit_dir,
int lmax)
270 using value_type =
typename VectorType1::Scalar;
272 value_type cp = (rxy) ? unit_dir[0]/rxy : 1.0;
273 value_type sp = (rxy) ? unit_dir[1]/rxy : 0.0;
274 return value (coefs, unit_dir[2], cp, sp, lmax);
278 template <
class VectorType1,
class VectorType2>
279 inline VectorType1&
delta (VectorType1& delta_vec,
const VectorType2& unit_dir,
int lmax)
281 using value_type =
typename VectorType1::Scalar;
282 delta_vec.resize (
NforL (lmax));
284 value_type cp = (rxy) ? unit_dir[0]/rxy : 1.0;
285 value_type sp = (rxy) ? unit_dir[1]/rxy : 0.0;
286 Eigen::Matrix<value_type,Eigen::Dynamic,1,0,64> AL (lmax+1);
288 for (
int l = 0; l <= lmax; l+=2)
289 delta_vec[
index (l,0)] = AL[l];
291 for (
int m = 1; m <= lmax; m++) {
295 for (
int l = ( (m&1) ? m+1 : m); l <= lmax; l+=2) {
307 template <
class VectorType1,
class VectorType2>
308 inline VectorType1&
SH2RH (VectorType1& RH,
const VectorType2& sh)
310 using value_type =
typename VectorType2::Scalar;
311 RH.resize (sh.size());
312 int lmax = 2*sh.size() +1;
313 Eigen::Matrix<value_type,Eigen::Dynamic,1,0,64> AL (lmax+1);
315 for (ssize_t l = 0; l < sh.size(); l++)
316 RH[l] = sh[l]/ AL[2*l];
320 template <
class VectorType>
321 inline Eigen::Matrix<typename VectorType::Scalar,Eigen::Dynamic,1>
SH2RH (
const VectorType& sh)
323 Eigen::Matrix<typename VectorType::Scalar,Eigen::Dynamic,1> RH (sh.size());
332 template <
class VectorType1,
class VectorType2>
333 inline VectorType1&
sconv (VectorType1& sh,
const VectorType2& RH)
335 assert (sh.size() >= ssize_t (
NforL (2* (RH.size()-1))));
336 for (ssize_t i = 0; i < RH.size(); ++i) {
338 for (
int m = -l; m <= l; ++m)
339 sh[
index (l,m)] *= RH[i];
348 template <
class VectorType1,
class VectorType2,
class VectorType3>
349 inline VectorType1&
sconv (VectorType1& C,
const VectorType2& RH,
const VectorType3& sh)
351 assert (sh.size() >= ssize_t (
NforL (2* (RH.size()-1))));
352 C.resize (
NforL (2* (RH.size()-1)));
353 for (ssize_t i = 0; i < RH.size(); ++i) {
355 for (
int m = -l; m <= l; ++m)
366 template <
class MatrixType1,
class VectorType2>
367 inline MatrixType1&
sconv_mat (MatrixType1& sh,
const VectorType2& RH)
369 assert (sh.cols() >= ssize_t (
NforL (2* (RH.size()-1))));
370 for (ssize_t i = 0; i < RH.size(); ++i) {
372 for (
int m = -l; m <= l; ++m)
373 sh.col(
index (l,m)) *= RH[i];
380 template <
typename>
struct __dummy {
NOMEMALIGN using type = int; };
406 init (up_to_lmax, num_dir);
412 operator bool ()
const {
416 void init (
int up_to_lmax,
int num_dir = 512) {
422 Eigen::Matrix<value_type,Eigen::Dynamic,1,0,64> buf (
lmax+1);
424 for (
int n = 0; n <
ndir; n++) {
427 for (
int m = 0; m <=
lmax; m++) {
429 for (
int l = ( (m&1) ?m+1:m); l <=
lmax; l+=2)
436 f.
f2 = elevation /
inc;
443 else if (i >=
ndir-1) {
458 ValueType v = f.
f1*f.
p1[i];
459 if (f.
f2) v += f.
f2*f.
p2[i];
467 for (
int l = 0; l <=
lmax; l+=2) {
468 for (
int m = 0; m <= l; m++) {
475 template <
class VectorType,
class UnitVectorType>
476 ValueType
value (
const VectorType& val,
const UnitVectorType& unit_dir)
const {
478 set (f, std::acos (unit_dir[2]));
479 ValueType rxy = std::sqrt (
pow2(unit_dir[1]) +
pow2(unit_dir[0]) );
480 ValueType cp = (rxy) ? unit_dir[0]/rxy : 1.0;
481 ValueType sp = (rxy) ? unit_dir[1]/rxy : 0.0;
483 for (
int l = 0; l <=
lmax; l+=2)
484 v +=
get (f,l,0) * val[
index (l,0)];
485 ValueType c0 (1.0), s0 (0.0);
486 for (
int m = 1; m <=
lmax; m++) {
487 ValueType c = c0 * cp - s0 * sp;
488 ValueType s = s0 * cp + c0 * sp;
489 for (
int l = ( (m&1) ? m+1 : m); l <=
lmax; l+=2)
514 template <
class VectorType,
class UnitVectorType,
class ValueType =
float>
516 const VectorType& sh,
518 UnitVectorType& unit_init_dir,
521 using value_type =
typename VectorType::Scalar;
522 assert (std::isfinite (unit_init_dir[0]));
523 for (
int i = 0; i < 50; i++) {
524 value_type az = std::atan2 (unit_init_dir[1], unit_init_dir[0]);
526 value_type amplitude, dSH_del, dSH_daz, d2SH_del2, d2SH_deldaz, d2SH_daz2;
527 derivatives (sh, lmax,
el, az, amplitude, dSH_del, dSH_daz, d2SH_del2, d2SH_deldaz, d2SH_daz2, precomputer);
529 value_type del = sqrt (dSH_del*dSH_del + dSH_daz*dSH_daz);
537 value_type dSH_dt = daz*dSH_daz + del*dSH_del;
538 value_type d2SH_dt2 = daz*daz*d2SH_daz2 + 2.0*daz*del*d2SH_deldaz + del*del*d2SH_del2;
539 value_type dt = d2SH_dt2 ? (-dSH_dt / d2SH_dt2) : 0.0;
541 if (dt < 0.0) dt = -dt;
547 unit_init_dir[0] += del*std::cos (az) *std::cos (
el) - daz*std::sin (az);
548 unit_init_dir[1] += del*std::sin (az) *std::cos (
el) + daz*std::cos (az);
549 unit_init_dir[2] -= del*std::sin (
el);
550 unit_init_dir.normalize();
557 DEBUG (
"failed to find SH peak!");
568 template <
class VectorType>
570 const VectorType& sh,
572 const typename VectorType::Scalar elevation,
573 const typename VectorType::Scalar azimuth,
574 typename VectorType::Scalar& amplitude,
575 typename VectorType::Scalar& dSH_del,
576 typename VectorType::Scalar& dSH_daz,
577 typename VectorType::Scalar& d2SH_del2,
578 typename VectorType::Scalar& d2SH_deldaz,
579 typename VectorType::Scalar& d2SH_daz2,
582 using value_type =
typename VectorType::Scalar;
585 bool atpole = sel < 1
e-4;
587 dSH_del = dSH_daz = d2SH_del2 = d2SH_deldaz = d2SH_daz2 = 0.0;
592 precomputer->
set (f, elevation);
593 precomputer->
get (AL, f);
596 Eigen::Matrix<value_type,Eigen::Dynamic,1,0,64> buf (lmax+1);
597 for (
int m = 0; m <= lmax; m++) {
599 for (
int l = ( (m&1) ?m+1:m); l <= lmax; l+=2)
604 amplitude = sh[0] * AL[0];
605 for (
int l = 2; l <= (int) lmax; l+=2) {
612 for (
int m = 1; m <= lmax; m++) {
615 for (
int l = ( (m&1) ? m+1 : m); l <= lmax; l+=2) {
618 amplitude += (vp*caz + vm*saz) * AL[
index_mpos (l,m)];
623 dSH_del += (vp*caz + vm*saz) * tmp;
626 if (m == 1) tmp2 -= sqrt (
value_type ( (l+m) * (l-m+1) * (l+m-1) * (l-m+2))) * AL[
index_mpos (l,1)];
628 if (l > m+1) tmp2 += sqrt (
value_type ( (l-m) * (l+m+1) * (l-m-1) * (l+m+2))) * AL[
index_mpos (l,m+2)];
630 d2SH_del2 += (vp*caz + vm*saz) * tmp2;
632 if (atpole) dSH_daz += (vm*caz - vp*saz) * tmp;
634 d2SH_deldaz += m * (vm*caz - vp*saz) * tmp;
635 dSH_daz += m * (vm*caz - vp*saz) * AL[
index_mpos (l,m)];
636 d2SH_daz2 -= (vp*caz + vm*saz) * m*m * AL[
index_mpos (l,m)];
645 d2SH_daz2 /= sel*sel;
652 template <
typename ValueType>
class aPSF
655 aPSF (
const size_t lmax) :
661 RH[0] = ValueType(1.00000000);
662 RH[1] = ValueType(0.41939279);
665 RH[0] = ValueType(1.00000000);
666 RH[1] = ValueType(0.63608543);
667 RH[2] = ValueType(0.18487087);
670 RH[0] = ValueType(1.00000000);
671 RH[1] = ValueType(0.75490341);
672 RH[2] = ValueType(0.37126442);
673 RH[3] = ValueType(0.09614699);
676 RH[0] = ValueType(1.00000000);
677 RH[1] = ValueType(0.82384816);
678 RH[2] = ValueType(0.51261696);
679 RH[3] = ValueType(0.22440563);
680 RH[4] = ValueType(0.05593079);
683 RH[0] = ValueType(1.00000000);
684 RH[1] = ValueType(0.86725945);
685 RH[2] = ValueType(0.61519436);
686 RH[3] = ValueType(0.34570667);
687 RH[4] = ValueType(0.14300355);
688 RH[5] = ValueType(0.03548062);
691 RH[0] = ValueType(1.00000000);
692 RH[1] = ValueType(0.89737759);
693 RH[2] = ValueType(0.69278503);
694 RH[3] = ValueType(0.45249879);
695 RH[4] = ValueType(0.24169922);
696 RH[5] = ValueType(0.09826171);
697 RH[6] = ValueType(0.02502481);
700 RH[0] = ValueType(1.00000000);
701 RH[1] = ValueType(0.91717853);
702 RH[2] = ValueType(0.74685644);
703 RH[3] = ValueType(0.53467773);
704 RH[4] = ValueType(0.33031863);
705 RH[5] = ValueType(0.17013825);
706 RH[6] = ValueType(0.06810155);
707 RH[7] = ValueType(0.01754930);
710 RH[0] = ValueType(1.00000000);
711 RH[1] = ValueType(0.93261196);
712 RH[2] = ValueType(0.79064858);
713 RH[3] = ValueType(0.60562880);
714 RH[4] = ValueType(0.41454703);
715 RH[5] = ValueType(0.24880754);
716 RH[6] = ValueType(0.12661242);
717 RH[7] = ValueType(0.05106681);
718 RH[8] = ValueType(0.01365433);
721 throw Exception (
"No aPSF RH data for lmax " +
str(lmax));
726 template <
class VectorType,
class UnitVectorType>
727 VectorType& operator() (VectorType& sh,
const UnitVectorType& dir)
const
729 sh.resize (RH.size());
730 delta (sh, dir, lmax);
731 return sconv (sh, RH);
734 inline const Eigen::Matrix<ValueType,Eigen::Dynamic,1>& RH_coefs()
const {
return RH; }
738 Eigen::Matrix<ValueType,Eigen::Dynamic,1> RH;
744 template <
class ImageType>
747 throw Exception (
"image \"" +
H.name() +
"\" does not contain SH coefficients - not 4D");
748 size_t l =
LforN (
H.size(3));
749 if (l%2 ||
NforL (l) !=
size_t (
H.size(3)))
750 throw Exception (
"image \"" +
H.name() +
"\" does not contain SH coefficients - unexpected number of coefficients");
Precomputed Associated Legrendre Polynomials - used to speed up SH calculation.
ValueType get(const PrecomputedFraction< ValueType > &f, int l, int m) const
void get(ValueType *dest, const PrecomputedFraction< ValueType > &f) const
void set(PrecomputedFraction< ValueType > &f, const ValueType elevation) const
ValueType get(const PrecomputedFraction< ValueType > &f, int i) const
PrecomputedAL(int up_to_lmax, int num_dir=512)
void init(int up_to_lmax, int num_dir=512)
ValueType value(const VectorType &val, const UnitVectorType &unit_dir) const
used to speed up SH calculation
vector< ValueType >::const_iterator p1
vector< ValueType >::const_iterator p2
a class to hold the coefficients for an apodised point-spread function.
#define VLA_MAX(name, type, num, max)
constexpr T pow2(const T &v)
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > pinv(const MatrixType &M)
return Moore-Penrose pseudo-inverse of M
void check(const ImageType &H)
convenience function to check if an input image can contain SH coefficients
size_t index(int l, int m)
compute the index for coefficient (l,m)
void scale_degrees_inverse(MatrixType &2SH_mapping, const VectorType &coefs)
scale the coefficients of each SH degree by the corresponding value in coefs
size_t LforN(int N)
returns the largest lmax given N parameters
void scale_degrees_forward(MatrixType &SH2amp_mapping, const VectorType &coefs)
scale the coefficients of each SH degree by the corresponding value in coefs
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > init_transform_cart(const MatrixType &dirs, const int lmax)
form the SH->amplitudes matrix
void derivatives(const VectorType &sh, const int lmax, const typename VectorType::Scalar elevation, const typename VectorType::Scalar azimuth, typename VectorType::Scalar &litude, typename VectorType::Scalar &dSH_del, typename VectorType::Scalar &dSH_daz, typename VectorType::Scalar &d2SH_del2, typename VectorType::Scalar &d2SH_deldaz, typename VectorType::Scalar &d2SH_daz2, PrecomputedAL< typename VectorType::Scalar > *precomputer)
computes first and second order derivatives of SH series
VectorType1 & sconv(VectorType1 &sh, const VectorType2 &RH)
perform spherical convolution, in place
size_t index_mpos(int l, int m)
same as index(), but consider only non-negative orders m
VectorType1 & delta(VectorType1 &delta_vec, const VectorType2 &unit_dir, int lmax)
size_t NforL_mpos(int lmax)
same as NforL(), but consider only non-negative orders m
size_t NforL(int lmax)
the number of (even-degree) coefficients for the given value of lmax
MatrixType1 & sconv_mat(MatrixType1 &sh, const VectorType2 &RH)
perform spherical convolution, in place
const char * encoding_description
a string containing a description of the SH storage convention
VectorType::Scalar get_peak(const VectorType &sh, int lmax, UnitVectorType &unit_init_dir, PrecomputedAL< typename VectorType::Scalar > *precomputer=nullptr)
estimate direction & amplitude of SH peak
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > init_transform(const MatrixType &dirs, const int lmax)
form the SH->amplitudes matrix
VectorType1 & SH2RH(VectorType1 &RH, const VectorType2 &sh)
Eigen::Matrix< typename VectorType::Scalar, Eigen::Dynamic, 1 > invert(const VectorType &coefs)
invert any non-zero coefficients in coefs
ValueType Plm_sph(const int l, const int m, const ValueType x)
MR::default_type value_type
Eigen::Matrix< value_type, Eigen::Dynamic, Eigen::Dynamic > matrix_type
std::string str(const T &value, int precision=0)
constexpr default_type NaN