Skip to content

Commit 48af567

Browse files
committed
[oneMKL samples][computed tomography] Add option to export cropped or uncropped images
1 parent b2d9a8a commit 48af567

File tree

1 file changed

+73
-24
lines changed

1 file changed

+73
-24
lines changed

Libraries/oneMKL/computed_tomography/computed_tomography.cpp

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ static const std::string default_original_bmpname = "input.bmp";
1818
static const std::string default_radon_bmpname = "radon.bmp";
1919
static const std::string default_restored_bmpname = "restored.bmp";
2020
static const std::string default_errors_bmpname = "errors.bmp";
21+
constexpr int default_crop = 1;
2122
#ifdef _WIN64
2223
static const std::string program_name = "compute_tomography.exe";
2324
#else
@@ -28,7 +29,7 @@ static const std::string usage_info =
2829
"\n\
2930
Usage:\n\
3031
======\n\
31-
" + program_name + " p q S_to_D in radon_out restored_out err_out\n\
32+
" + program_name + " p q S_to_D in radon_out restored_out err_out crop\n\
3233
Inputs:\n\
3334
-------\n\
3435
p - number of projection directions considered for the Radon\n\
@@ -48,6 +49,11 @@ static const std::string usage_info =
4849
in - name of the input image used to generate Radon transform data.\n\
4950
The file must be a 24-bit uncompressed bitmap file.\n\
5051
\"" + default_original_bmpname + "\" is considered by default.\n\
52+
crop - integer flag indicating whether to crop the generated bitmap\n\
53+
output images to their range of relevance or not. Images are\n\
54+
(resp. are not) cropped if the value is 1 (resp. 0).\n\
55+
The supported values are 0 and 1 (default value is "
56+
+ std::to_string(default_crop) + ").\n\
5157
Outputs:\n\
5258
--------\n\
5359
radon_out - name of a 24-bit uncompressed bitmap image file storing a\n\
@@ -205,26 +211,38 @@ double bmp_read(padded_matrix &image, const std::string& fname) {
205211
fp.close();
206212
return max_gray_scale;
207213
}
208-
// Routine exporting a padded_matrix (~array of doubles) as a gray-scale 24-bit
209-
// uncompressed bitmap image file.
210-
// For every entry v of the matrix, a gray scale (max(v, 0.0) / max_abs) is
211-
// calculated and the corresponding pixel's color code is generated.
212-
// Note: negative values are ignored (considered white) by this routine. While
213-
// negative values may be found in the reconstructed image, they should be
214-
// considered artifacts (e.g., due to Gibbs phenomenon) and/or local errors of
215-
// the reconstruction procedure (e.g., due to the rudimentary spectrum
214+
// Routine exporting the values image.data[i*image.ldw() + j] of a
215+
// padded_matrix "image" for indices (i, j) such that
216+
// max(min_max_i[0], 0) <= i < min(min_max_i[1], image.h)
217+
// max(min_max_j[0], 0) <= j < min(min_max_j[1], image.w)
218+
// as a gray-scale 24-bit uncompressed bitmap image file.
219+
// For every such relevant entry v of the matrix, a gray scale value
220+
// proportional to max(v, 0.0) is calculated (maximizing contrast) and the
221+
// corresponding pixel's color code is generated. In other words, negative
222+
// values are ignored (considered white) by this routine.
223+
// Note: while negative values may be found in the reconstructed image, they
224+
// may be considered artifacts (e.g., due to Gibbs phenomenon) and/or local
225+
// errors of the reconstruction procedure (e.g., due to the rudimentary spectrum
216226
// interpolation).
217-
void bmp_write(const std::string& fname, const padded_matrix &image) {
218-
unsigned sizeof_line = (image.w * 3 + 3) / 4 * 4;
219-
unsigned sizeof_image = image.h * sizeof_line;
227+
void bmp_write(const std::string& fname, const padded_matrix &image,
228+
const int min_max_i[2], const int min_max_j[2]) {
229+
const int min_i = std::max(min_max_i[0], 0);
230+
const int max_i = std::min(min_max_i[1], image.h);
231+
const int min_j = std::max(min_max_j[0], 0);
232+
const int max_j = std::min(min_max_j[1], image.w);
233+
if (max_i <= min_i || max_j <= min_j)
234+
die("invalid range of pixel indices for bmp_write to export");
235+
236+
unsigned sizeof_line = ((max_j - min_j) * 3 + 3) / 4 * 4;
237+
unsigned sizeof_image = (max_i - min_i) * sizeof_line;
220238

221239
bmp_header header = {{'B', 'M'},
222240
unsigned(sizeof(header) + sizeof_image),
223241
0,
224242
sizeof(header),
225243
sizeof(header) - offsetof(bmp_header, bi_size),
226-
unsigned(image.w),
227-
unsigned(image.h),
244+
unsigned(max_j - min_j),
245+
unsigned(max_i - min_i),
228246
1,
229247
24,
230248
0,
@@ -241,25 +259,25 @@ void bmp_write(const std::string& fname, const padded_matrix &image) {
241259

242260
fp.write((char *)(&header), sizeof(header));
243261
double v_max = std::numeric_limits<double>::lowest();
244-
for (int i = 0; i < image.h; ++i)
245-
for (int j = 0; j < image.w; ++j)
262+
for (int i = min_i; i < max_i; ++i)
263+
for (int j = min_j; j < max_j; ++j)
246264
v_max = std::max(image.data[i * image.ldw() + j], v_max);
247265

248266
if (v_max <= 0.0) {
249267
fp.close();
250268
die("inconsistent data range to consider for exporting " + fname);
251269
}
252270
pixel pix;
253-
for (int i = 0; i < image.h; ++i) {
254-
for (int j = 0; j < image.w; ++j) {
271+
for (int i = min_i; i < max_i; ++i) {
272+
for (int j = min_j; j < max_j; ++j) {
255273
const double gray =
256274
std::max(image.data[i * image.ldw() + j], 0.0)/v_max;
257275
pix.b = pix.g = pix.r =
258276
static_cast<unsigned char>(std::round(255.0*(1.0 - gray)));
259277
fp.write((char *)(&pix), 3);
260278
}
261279
// rows are rounded up to a multiple of 4 bytes in BMP format
262-
for (int j = 3 * image.w; j % 4; ++j)
280+
for (int j = 3 * (max_j - min_j); j % 4; ++j)
263281
fp.put(0);
264282
}
265283
fp.close();
@@ -819,10 +837,17 @@ int main(int argc, char **argv) {
819837
default_restored_bmpname;
820838
const std::string errors_bmpname = argc > 7 ? argv[7] :
821839
default_errors_bmpname;
840+
const int crop = argc > 8 ? std::atoi(argv[8]) :
841+
default_crop;
822842
// validate numerical input arguments
823-
if (argc > 8 || p <= 0 || q <= 0 || S_to_D < 1.0) {
843+
if (argc > 9 || p <= 0 || q <= 0 || S_to_D < 1.0 || crop < 0 || crop > 1) {
824844
die("invalid usage.\n" + usage_info);
825845
}
846+
// range of pixel indices to consider when exporting an image.
847+
int i_range[2] = {std::numeric_limits<int>::lowest(),
848+
std::numeric_limits<int>::max()};
849+
int j_range[2] = {std::numeric_limits<int>::lowest(),
850+
std::numeric_limits<int>::max()};
826851

827852
// Create execution queue.
828853
sycl::queue main_queue;
@@ -839,7 +864,9 @@ int main(int argc, char **argv) {
839864
padded_matrix original(main_queue);
840865
const double max_input_value = bmp_read(original, original_bmpname);
841866
// diagonal D of original image
842-
const double D = std::hypot(original.h, original.w)*in_pix_len;
867+
const double ww = original.w*in_pix_len;
868+
const double hh = original.h*in_pix_len;
869+
const double D = std::hypot(hh, ww);
843870
// scanning width S
844871
const double S = S_to_D * D;
845872
// Compute samples of the radon transform
@@ -850,15 +877,36 @@ int main(int argc, char **argv) {
850877
std::cout << "Generating Radon transform data from "
851878
<< original_bmpname << std::endl;
852879
auto radon_ev = acquire_radon(radon_image, S, original);
880+
if (crop == 1) {
881+
// values of radon_image.data[i*radon_image.ldw() + j] are expected to
882+
// be 0.0 if |(-1.0 + (2.0*j + 1.0)/q)*S_to_D| > 1
883+
i_range[0] = 0;
884+
i_range[1] = radon_image.h;
885+
j_range[0] =
886+
static_cast<int>(std::ceil(0.5*((1.0 - 1.0/S_to_D)*q - 1.0)));
887+
j_range[1] =
888+
static_cast<int>(std::ceil(0.5*((1.0 + 1.0/S_to_D)*q - 1.0)));
889+
}
853890
std::cout << "Saving Radon transform data in "
854891
<< radon_bmpname << std::endl;
855892
radon_ev.wait(); // make sure it completes before exporting data
856-
bmp_write(radon_bmpname, radon_image);
893+
bmp_write(radon_bmpname, radon_image, i_range, j_range);
857894
// reconstruct image from its radon transform samples
858895
padded_matrix reconstruction(main_queue);
859896
reconstruction_from_radon(reconstruction, radon_image, S, radon_ev);
897+
if (crop == 1) {
898+
// values of reconstruction.data[i*reconstruction.ldw() + j] are
899+
// out of the relevant range of comparison if
900+
// |(i - q/2)*S/q| - 0.5*S/q > 0.5*hh
901+
// or
902+
// |(j - q/2)*S/q| - 0.5*S/q > 0.5*ww
903+
i_range[0] = static_cast<int>(std::ceil(-0.5*hh*q/S + q/2 - 0.5));
904+
i_range[1] = static_cast<int>(std::ceil(+0.5*hh*q/S + q/2 + 0.5));
905+
j_range[0] = static_cast<int>(std::ceil(-0.5*ww*q/S + q/2 - 0.5));
906+
j_range[1] = static_cast<int>(std::ceil(+0.5*ww*q/S + q/2 + 0.5));
907+
}
860908
std::cout << "Saving restored image in " << restored_bmpname << std::endl;
861-
bmp_write(restored_bmpname, reconstruction);
909+
bmp_write(restored_bmpname, reconstruction, i_range, j_range);
862910
// evaluate the mean error, pixel by pixel in the reconstructed image
863911
padded_matrix errors(main_queue);
864912
const double mean_error = compute_errors(errors, S, original, reconstruction);
@@ -886,7 +934,8 @@ int main(int argc, char **argv) {
886934
}
887935
std::cerr << "Saving local errors in " << errors_bmpname
888936
<< std::endl << std::flush;
889-
bmp_write(errors_bmpname, errors);
937+
// same relevant pixel indices for errors as for reconstruction
938+
bmp_write(errors_bmpname, errors, i_range, j_range);
890939
return EXIT_FAILURE;
891940
}
892941

0 commit comments

Comments
 (0)