@@ -18,6 +18,7 @@ static const std::string default_original_bmpname = "input.bmp";
18
18
static const std::string default_radon_bmpname = " radon.bmp" ;
19
19
static const std::string default_restored_bmpname = " restored.bmp" ;
20
20
static const std::string default_errors_bmpname = " errors.bmp" ;
21
+ constexpr int default_crop = 1 ;
21
22
#ifdef _WIN64
22
23
static const std::string program_name = " compute_tomography.exe" ;
23
24
#else
@@ -28,7 +29,7 @@ static const std::string usage_info =
28
29
" \n \
29
30
Usage:\n \
30
31
======\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 \
32
33
Inputs:\n \
33
34
-------\n \
34
35
p - number of projection directions considered for the Radon\n \
@@ -48,6 +49,11 @@ static const std::string usage_info =
48
49
in - name of the input image used to generate Radon transform data.\n\
49
50
The file must be a 24-bit uncompressed bitmap file.\n\
50
51
\"" + 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\
51
57
Outputs:\n\
52
58
--------\n\
53
59
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) {
205
211
fp.close ();
206
212
return max_gray_scale;
207
213
}
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
216
226
// 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;
220
238
221
239
bmp_header header = {{' B' , ' M' },
222
240
unsigned (sizeof (header) + sizeof_image),
223
241
0 ,
224
242
sizeof (header),
225
243
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 ),
228
246
1 ,
229
247
24 ,
230
248
0 ,
@@ -241,25 +259,25 @@ void bmp_write(const std::string& fname, const padded_matrix &image) {
241
259
242
260
fp.write ((char *)(&header), sizeof (header));
243
261
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)
246
264
v_max = std::max (image.data [i * image.ldw () + j], v_max);
247
265
248
266
if (v_max <= 0.0 ) {
249
267
fp.close ();
250
268
die (" inconsistent data range to consider for exporting " + fname);
251
269
}
252
270
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) {
255
273
const double gray =
256
274
std::max (image.data [i * image.ldw () + j], 0.0 )/v_max;
257
275
pix.b = pix.g = pix.r =
258
276
static_cast <unsigned char >(std::round (255.0 *(1.0 - gray)));
259
277
fp.write ((char *)(&pix), 3 );
260
278
}
261
279
// 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)
263
281
fp.put (0 );
264
282
}
265
283
fp.close ();
@@ -819,10 +837,17 @@ int main(int argc, char **argv) {
819
837
default_restored_bmpname;
820
838
const std::string errors_bmpname = argc > 7 ? argv[7 ] :
821
839
default_errors_bmpname;
840
+ const int crop = argc > 8 ? std::atoi (argv[8 ]) :
841
+ default_crop;
822
842
// 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 ) {
824
844
die (" invalid usage.\n " + usage_info);
825
845
}
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 ()};
826
851
827
852
// Create execution queue.
828
853
sycl::queue main_queue;
@@ -839,7 +864,9 @@ int main(int argc, char **argv) {
839
864
padded_matrix original (main_queue);
840
865
const double max_input_value = bmp_read (original, original_bmpname);
841
866
// 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);
843
870
// scanning width S
844
871
const double S = S_to_D * D;
845
872
// Compute samples of the radon transform
@@ -850,15 +877,36 @@ int main(int argc, char **argv) {
850
877
std::cout << " Generating Radon transform data from "
851
878
<< original_bmpname << std::endl;
852
879
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
+ }
853
890
std::cout << " Saving Radon transform data in "
854
891
<< radon_bmpname << std::endl;
855
892
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 );
857
894
// reconstruct image from its radon transform samples
858
895
padded_matrix reconstruction (main_queue);
859
896
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
+ }
860
908
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 );
862
910
// evaluate the mean error, pixel by pixel in the reconstructed image
863
911
padded_matrix errors (main_queue);
864
912
const double mean_error = compute_errors (errors, S, original, reconstruction);
@@ -886,7 +934,8 @@ int main(int argc, char **argv) {
886
934
}
887
935
std::cerr << " Saving local errors in " << errors_bmpname
888
936
<< 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);
890
939
return EXIT_FAILURE;
891
940
}
892
941
0 commit comments