Skip to content

Commit cd1f7aa

Browse files
committed
[oneMKL samples][computed tomography] Simplifying comments, unifying notations, slight modification to discrete heaviside
1 parent 7b77e8d commit cd1f7aa

File tree

1 file changed

+66
-85
lines changed

1 file changed

+66
-85
lines changed

Libraries/oneMKL/computed_tomography/computed_tomography.cpp

Lines changed: 66 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,9 @@ void reconstruction_from_radon(padded_matrix& image,
516516
By the Fourier slice theorem,
517517
radon_hat(theta, ksi) = f_hat(ksi*sin(theta), ksi*cos(theta))
518518
where f_hat is the (continuous) 2D Fourier transform of f, i.e.,
519-
f_hat(k_y, k_x) :=
520-
integral integral (f(y,x)*exp(-1i*2*M_PI*(k_y*y + k_x*x))) dx dy.
521-
y real x real
519+
f_hat(kappa_y, kappa_x) :=
520+
integral integral (f(y,x)*exp(-1i*2*M_PI*(kappa_y*y + kappa_x*x))) dx dy.
521+
y real x real
522522
523523
Considering ksi = r / S for integers r in [0, q/2], one has
524524
radon_hat(theta(i), r / S)
@@ -527,7 +527,7 @@ void reconstruction_from_radon(padded_matrix& image,
527527
which may be approximated as (midpoint rule)
528528
radon_hat(theta(i), r / S)
529529
~ \sum_j (R[i][j]*exp(-1i*2*M_PI*(r/S)*v(j))) * (S/q), for j=0,...,q-1
530-
= exp(1i*M_PI*r*(1.0 - 1.0/q)) * (S/q) * DFT(R[i][0:q[ ; r), (eq. 1a)
530+
= exp(1i*M_PI*r*(1.0 - 1.0/q)) * (S/q) * DFT(R[i][0:q[; r), (eq. 1a)
531531
where DFT(R[i][0:q[; r) represents the r-th value of the forward DFT of
532532
the i-th row of q discrete values in R.
533533
Note: radon_hat(theta(i) + M_PI, r / S) = conj(radon_hat(theta(i), r / S))
@@ -542,7 +542,7 @@ void reconstruction_from_radon(padded_matrix& image,
542542
// Distances must be set for batched transforms. For real in-place DFTs with
543543
// unit stride, the distance in forward domain (wherein elements are real)
544544
// must be twice the distance in backward domain (wherein elements are
545-
// complex). Therefore, padding is requied in forward domain (as accounted
545+
// complex). Therefore, padding is required in forward domain (as accounted
546546
// for by the padded_matrix structure)
547547
radon_dft.set_value(dft_ns::config_param::FWD_DISTANCE, R.ldw());
548548
radon_dft.set_value(dft_ns::config_param::BWD_DISTANCE, R.ldw() / 2);
@@ -559,128 +559,109 @@ void reconstruction_from_radon(padded_matrix& image,
559559
Note that values of i in [p, 2*p) are known too, as the complex conjugates
560560
of their (i-p) counterparts (albeit not stored explicitly).
561561
562-
The reconstructed image is based off a version of the spectrum f_hat that
563-
is truncated to wave vectors no larger than 0.5*q/S in magnitude, e.g.,
564-
g_hat(ksi_y, ksi_x) =
565-
f_hat(ksi_y, ksi_x)*heaviside(0.5*q/S - sqrt(ksi_y^2 + ksi_x^2)),
566-
where heaviside(x) := {0, 0.5, 1} if {x < 0.0, x == 0, x > 0.0},
567-
respectively.
568-
569-
While g(y, x) is not strictly equal to f(y, x) pointwise in general, g
570-
converges towards f in a weaker sense (e.g., in L2 norm) as q gets large if
571-
f is bounded and has compact support. Since samples of g are desired at
572-
specific points, i.e., the (centers of the) q x q pixels of the
573-
reconstructed S-by-S image, let
574-
g_s(y, x) = g(y, x) * 2d_dirac_comb(y, x; S/q),
575-
where 2d_dirac_comb(y, x; alpha) is defined as
576-
\sum_j \sum_i 2d_dirac(y-alpha*j, x-alpha*i), {i,j} = -infty,...,+infty.
577-
578-
By the convolution theorem, the spectrum of g_s is
579-
g_s_hat(ksi_y, ksi_x) = (eq. 2)
580-
(q^2/S^2)*convolution[g_hat(., .), 2d_dirac_comb(., .; q/S)](ksi_y, ksi_x),
581-
which is periodic with period q/S along ksi_y and ksi_x. Sampling g_s_hat at
582-
wave vectors of components multiple of 1.0/S, i.e., considering
583-
f_tilde_hat(ksi_y, ksi_x) = (eq. 3)
584-
g_s_hat(ksi_y, ksi_x) * 2d_dirac_comb(ksi_y, ksi_x; 1.0/S)
585-
effectively corresponds to f_tilde(y, x) being a periodic replication of
586-
(S^2)*g_s(y, x) with period S along y and x (assuming that values of
587-
g_s(y, x) may be ignored for {(y, x): max(|y|, |x|) > 0.5*S}).
588-
589-
Developing the (continuous) 2D inverse Fourier transform of f_tilde_hat
590-
(see eq. 3), one has
591-
f_tilde(y, x) = \sum_i \sum_j 2d_dirac(y-i*(S/q), x-j*(S/q)) G[i%q][j%q],
592-
{i,j} = -infty,...,+infty
593-
where G[i][j] (0 <= i < q. 0 <= j < q) is the (i,j) value of the 2D backward
594-
DFT of
595-
G_S_HAT[m][n] = g_s_hat(mm(m)/S, nn(n)/S), 0 <= m < q, 0 <= n < q
596-
where 0 <= |xx(x)| <= q/2 and mod(x, q) == mod(xx(x), q) with 'x' being
597-
either 'm' or 'n'. Explicitly, for 0 <= m < q, 0 <= n < q, let
598-
abs_S_ksi = sycl::hypot(mm(n), nn(n));
599-
theta = sycl::atan2(mm(n), nn(n));
600-
then (see eq. 2)
601-
G_S_HAT[m][n]=0.0, if abs_S_ksi > 0.5*q;
602-
G_S_HAT[m][n]=f_hat(mm(m)/S, nn(n)/S), if round(abs_S_ksi) < 0.5*q;
603-
G_S_HAT[m][n]=0.5*f_hat(mm(m)/S, nn(n)/S), if round(abs_S_ksi) = 0.5*q,
604-
and fmod(theta, 0.5*M_PI) != 0.0;
605-
G_S_HAT[m][n]=real_part(f_hat(mm(m)/S, nn(n)/S)),
606-
if round(abs_S_ksi) = 0.5*q,
607-
and fmod(theta, 0.5*M_PI) = 0.0.
608-
609-
Identifying f_tilde(y, x) as the periodic replication of (S^2)*g_s(y, x)
610-
with period S along y and x, one concludes that
611-
g(i*(S/q), j*(S/q)) = G[i%q][j%q] / S^2 (eq. 4)
612-
for integers i and j s.t. |i| <= q/2 and |j| <= q/2 (assuming that values of
613-
g_s(y, x) may be ignored for {(y, x): max(|y|, |x|) > 0.5*S}).
614-
615-
The required values of G_S_HAT[m][n] are interpolated below from the known
562+
The reconstructed image approaches (an elementary cell of) the periodic
563+
replication of f of period S along y and x, i.e., of
564+
+\infty +\infty
565+
\psi(y, x) = \sum \sum f(y - n_y*S, x - n_x*S).
566+
n_y=-\infty n_x=-\infty
567+
Given its periodic nature, \psi(y, x) can be expressed as the Fourier series
568+
+\infty +\infty
569+
\sum \sum \psi_hat[k_y, k_x]*exp(1i*2*M_PI*(k_y*y+k_x*x)/S),
570+
k_y=-\infty k_x=-\infty
571+
wherein
572+
\psi_hat[k_y, k_x] = (1/S^2)*f_hat(k_y/S, k_x/S).
573+
574+
Given that f_hat(k_y/S, k_x/S) is unknown for hypot(k_x, k_y) > q/2, the
575+
reconstruction function g approximates \psi as its truncated Fourier series,
576+
i.e.,
577+
(S^2)*g(y,x) = \sum f_hat(k_y/S, k_x/S)*exp(1i*2*M_PI*(k_y*y+k_x*x)/S).
578+
[k_y,k_x],
579+
hypot(k_y, k_x) <= q/2
580+
581+
Sampling the values of g at points (i*S/q, j*S/q) (0 <= i < q and 0 <= j < q,
582+
i and j are integers), one has
583+
G[i,j] = (1/S^2)*iDFT(G_HAT; i,j) (eq. 2)
584+
where G_HAT is a set of qxq (complex) values G_HAT[m][n] (0 <= m < q and
585+
0 <= n < q, m and n are integers) such that
586+
G_HAT[m][n] = 0.0, if hypot(mm, nn) > 0.5*q;
587+
G_HAT[m][n] = f_hat(mm/S, nn/S) if hypot(mm, nn) < 0.5*q
588+
or if (hypot(mm, nn) == 0.5*q
589+
&& mm != 0 && nn != 0)
590+
G_HAT[m][n] = 2*real_part(f_hat(mm/S, nn/S)), if (hypot(mm, nn) == 0.5*q
591+
&& (mm != 0 || nn != 0)).
592+
where the notation "xx" represents an integer such that
593+
0 <= |xx| <= q/2 and mod(x, q) == mod(xx, q)
594+
('x' being either 'm' or 'n' above).
595+
596+
The required values of G_HAT[m][n] are interpolated below from the known
616597
values stored in R (using eq. 1b).
617-
Note that G_S_HAT[q - m][q - n] = conj(G_S_HAT[m][n]) given that
618-
f_hat(-ksi_y, -ksi_x) = conj(f_hat(ksi_y, ksi_x)). This is consistent with
619-
the requirements for a well-defined backward real 2D DFT, and the values of
620-
G_S_HAT[m][n] do not need to be set explicitly for n > q/2.
598+
Note that G_HAT[q - m][q - n] = conj(G_HAT[m][n]) by construction, given
599+
that f_hat(-ksi_y, -ksi_x) = conj(f_hat(ksi_y, ksi_x)). This is consistent
600+
with the requirements for a well-defined backward real 2D DFT. Therefore, the
601+
values of G_HAT[m][n] do not need to be set/stored explicitly for n > q/2.
621602
*/
622603
std::cout << "\tStep 2 - Interpolating spectrum from polar to "
623604
<< "cartesian grid" << std::endl;
624605
auto interp_ev = image.q.submit([&](sycl::handler &cgh) {
625606
cgh.depends_on(compute_radon_hat);
626607
const complex_t *R_data_c = reinterpret_cast<complex_t*>(R.data);
627-
complex_t *G_S_HAT = reinterpret_cast<complex_t*>(image.data);
608+
complex_t *G_HAT = reinterpret_cast<complex_t*>(image.data);
628609
const int R_data_c_ldw = R.ldw()/2; // in complex values
629-
const int G_S_HAT_ldw = image.ldw()/2; // in complex values
610+
const int G_HAT_ldw = image.ldw()/2; // in complex values
630611
cgh.parallel_for<class interpolateKernelClass>(
631612
sycl::range<2>(q, q/2 + 1),
632613
[=](sycl::item<2> item) {
633614
const int m = item.get_id(0);
634615
const int n = item.get_id(1);
635616
const int mm = m - (2*m > q ? q : 0);
636617
const int nn = n; // nn(n) == n since n never exceeds q/2
637-
const double abs_S_ksi = sycl::hypot(double(mm), double(nn));
618+
const double abs_k = sycl::hypot(double(mm), double(nn));
638619
const double theta = (mm == 0 && nn == 0) ?
639620
0.0 : sycl::atan2(double(mm), double(nn));
640621
// theta in [-0.5*M_PI, +0.5*M_PI]
641-
complex_t G_S_HAT_mn = complex_t(0.0, 0.0);
642-
if (abs_S_ksi <= 0.5*q) {
643-
const int r = static_cast<int>(sycl::round(abs_S_ksi));
622+
complex_t G_HAT_mn = complex_t(0.0, 0.0);
623+
if (abs_k <= 0.5*q) {
624+
const int r = static_cast<int>(sycl::round(abs_k));
644625
const int i =
645626
static_cast<int>(sycl::round(((theta + 0.5*M_PI)/M_PI)*p));
646627
// if i < 0 or i >= p (e.g., theta = 0.5*M_PI corresponds to
647628
// i == p), the value is mapped to the complex conjugate of
648629
// R_data_c[(i%p) * R_data_c_ldw + r] (e.g.,
649630
// theta = -0.5*M_PI if i == p).
650-
complex_t f_hat_mm_nn =
631+
// Approximated values of
632+
// f_hat((r/S)*sin(theta(i)), (r/S)*cos(theta(i))):
633+
complex_t f_hat_value =
651634
R_data_c[(i% p) * R_data_c_ldw + r]*
652635
complex_t(sycl::cos(M_PI*r*(1.0 - 1.0/q)),
653636
sycl::sin(M_PI*r*(1.0 - 1.0/q))); // see eq. 1b
654637
if (i%(2*p) >= p)
655-
f_hat_mm_nn = std::conj(f_hat_mm_nn);
656-
657-
G_S_HAT_mn = f_hat_mm_nn;
658-
if (2*r == q) {
659-
if (nn == 0 || mm == 0) // |theta| = 0.0 or 0.5*M_PI
660-
G_S_HAT_mn.imag(0.0);
661-
else
662-
G_S_HAT_mn *= 0.5;
638+
f_hat_value = std::conj(f_hat_value);
639+
640+
G_HAT_mn = f_hat_value;
641+
if (2*r == q && (nn == 0 || mm == 0)) {
642+
G_HAT_mn *= 2.0;
643+
G_HAT_mn.imag(0.0);
663644
}
664645
// For a more convenient representation of the reconstructed
665646
// image, shift the target reference frame so that the
666647
// center of the reconstructed image is located at pixel
667648
// of indices (q/2, q/2):
668-
G_S_HAT_mn *= complex_t(sycl::cos(-2.0*M_PI*m*(q/2)/q),
669-
sycl::sin(-2.0*M_PI*m*(q/2)/q));
649+
G_HAT_mn *= complex_t(sycl::cos(-2.0*M_PI*m*(q/2)/q),
650+
sycl::sin(-2.0*M_PI*m*(q/2)/q));
670651
// RHS is ((-1)^m, 0) is q is even
671-
G_S_HAT_mn *= complex_t(sycl::cos(-2.0*M_PI*n*(q/2)/q),
672-
sycl::sin(-2.0*M_PI*n*(q/2)/q));
652+
G_HAT_mn *= complex_t(sycl::cos(-2.0*M_PI*n*(q/2)/q),
653+
sycl::sin(-2.0*M_PI*n*(q/2)/q));
673654
// RHS is ((-1)^n, 0) is q is even
674655
}
675-
G_S_HAT[m * G_S_HAT_ldw + n] = G_S_HAT_mn;
656+
G_HAT[m * G_HAT_ldw + n] = G_HAT_mn;
676657
});
677658
});
678659
std::cout << "\tStep 3 - In-place backward real 2D DFT of size "
679660
<< q << "x" << q << std::endl;
680661
real_descriptor_t q_by_q_real_dft({q, q});
681662
// Default strides are set by default for in-place DFTs (consistently with
682663
// the implementation of padded_matrix)
683-
// Scaling factor for backward DFT (see eq. 4)
664+
// Scaling factor for backward DFT (see eq. 2)
684665
q_by_q_real_dft.set_value(dft_ns::config_param::BACKWARD_SCALE, 1.0 / (S*S));
685666
q_by_q_real_dft.commit(image.q);
686667
auto compute_g_values =
@@ -693,7 +674,7 @@ void reconstruction_from_radon(padded_matrix& image,
693674
// original image. The mean error over an area A is defined as
694675
// (1.0/area(A)) integral |f_reconstruction - f_original| dA
695676
// A
696-
// where f_reconstruction (resp. f_original) is the piecewise contant function
677+
// where f_reconstruction (resp. f_original) is the piecewise constant function
697678
// of the gray-scale intensity in the reconstructed (resp. original) image.
698679
// Note: f_reconstruction and f_original are considered equal to 0.0 outside
699680
// the support of the original image.

0 commit comments

Comments
 (0)