2 // Copyright 2005-2007 Adobe Systems Incorporated
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
9 #pragma warning(disable : 4244) // conversion from 'gil::image<V,Alloc>::coord_t' to 'int', possible loss of data (visual studio compiler doesn't realize that the two types are the same)
10 #pragma warning(disable : 4503) // decorated name length exceeded, name was truncated
11 #pragma warning(disable : 4701) // potentially uninitialized local variable 'result' used in boost/crc.hpp
14 #include <boost/gil.hpp>
15 #include <boost/gil/extension/dynamic_image/dynamic_image_all.hpp>
17 #include <boost/core/ignore_unused.hpp>
18 #include <boost/crc.hpp>
19 #include <boost/mp11.hpp>
27 #include <type_traits>
30 using namespace boost::gil
;
32 using namespace boost
;
34 extern rgb8c_planar_view_t sample_view
;
35 void error_if(bool condition
);
37 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
39 #pragma warning(disable:4127) //conditional expression is constant
42 // When BOOST_GIL_GENERATE_REFERENCE_DATA is defined, the reference data is generated and saved.
43 // When it is undefined, regression tests are checked against it
44 //#define BOOST_GIL_GENERATE_REFERENCE_DATA
46 ////////////////////////////////////////////////////
48 /// Some algorithms to use in testing
50 ////////////////////////////////////////////////////
52 template <typename GrayView
, typename R
>
53 void gray_image_hist(GrayView
const& img_view
, R
& hist
)
55 for (auto it
= img_view
.begin(); it
!= img_view
.end(); ++it
)
58 // Alternatively, prefer the algorithm with lambda
59 // for_each_pixel(img_view, [&hist](gray8_pixel_t const& pixel) {
64 template <typename V
, typename R
>
65 void get_hist(const V
& img_view
, R
& hist
) {
66 gray_image_hist(color_converted_view
<gray8_pixel_t
>(img_view
), hist
);
69 // testing custom color conversion
70 template <typename C1
, typename C2
>
71 struct my_color_converter_impl
: public default_color_converter_impl
<C1
,C2
> {};
72 template <typename C1
>
73 struct my_color_converter_impl
<C1
,gray_t
> {
74 template <typename P1
, typename P2
>
75 void operator()(const P1
& src
, P2
& dst
) const {
76 default_color_converter_impl
<C1
,gray_t
>()(src
,dst
);
77 get_color(dst
,gray_color_t())=channel_invert(get_color(dst
,gray_color_t()));
81 struct my_color_converter
{
82 template <typename SrcP
,typename DstP
>
83 void operator()(const SrcP
& src
,DstP
& dst
) const {
84 using src_cs_t
= typename color_space_type
<SrcP
>::type
;
85 using dst_cs_t
= typename color_space_type
<DstP
>::type
;
86 my_color_converter_impl
<src_cs_t
,dst_cs_t
>()(src
,dst
);
90 /// Models a Unary Function
91 /// \tparam P models PixelValueConcept
95 using point_t
= boost::gil::point_t
;
96 using const_t
= mandelbrot_fn
<P
>;
98 using reference
= value_type
;
99 using const_reference
= value_type
;
100 using argument_type
= point_t
;
101 using result_type
= reference
;
102 static constexpr bool is_mutable
= false;
104 value_type _in_color
,_out_color
;
106 static const int MAX_ITER
=100; // max number of iterations
109 mandelbrot_fn(const point_t
& sz
, const value_type
& in_color
, const value_type
& out_color
) : _in_color(in_color
), _out_color(out_color
), _img_size(sz
) {}
111 result_type
operator()(const point_t
& p
) const {
112 // normalize the coords to (-2..1, -1.5..1.5)
113 // (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods)
114 double t
=get_num_iter(point
<double>(p
.x
/(double)_img_size
.x
*3-2, p
.y
/(double)_img_size
.y
*3-1.0f
));//1.5f));
118 for (int k
=0; k
<num_channels
<P
>::value
; ++k
)
119 ret
[k
]=(typename channel_type
<value_type
>::type
)(_in_color
[k
]*t
+ _out_color
[k
]*(1-t
));
124 double get_num_iter(const point
<double>& p
) const {
125 point
<double> Z(0,0);
126 for (int i
=0; i
<MAX_ITER
; ++i
) {
127 Z
= point
<double>(Z
.x
*Z
.x
- Z
.y
*Z
.y
+ p
.x
, 2*Z
.x
*Z
.y
+ p
.y
);
128 if (Z
.x
*Z
.x
+ Z
.y
*Z
.y
> 4)
129 return i
/(double)MAX_ITER
;
135 template <typename T
>
136 void x_gradient(const T
& src
, const gray8s_view_t
& dst
) {
137 for (int y
=0; y
<src
.height(); ++y
) {
138 typename
T::x_iterator src_it
= src
.row_begin(y
);
139 gray8s_view_t::x_iterator dst_it
= dst
.row_begin(y
);
141 for (int x
=1; x
<src
.width()-1; ++x
)
142 dst_it
[x
] = (src_it
[x
+1] - src_it
[x
-1]) / 2;
146 // A quick test whether a view is homogeneous
148 template <typename Pixel
>
149 struct pixel_is_homogeneous
: public std::true_type
{};
151 template <typename P
, typename C
, typename L
>
152 struct pixel_is_homogeneous
<packed_pixel
<P
,C
,L
> > : public std::false_type
{};
154 template <typename View
>
155 struct view_is_homogeneous
: public pixel_is_homogeneous
<typename
View::value_type
> {};
157 ////////////////////////////////////////////////////
159 /// Tests image view transformations and algorithms
161 ////////////////////////////////////////////////////
164 virtual void initialize() {}
165 virtual void finalize() {}
166 virtual ~image_test() {}
170 virtual void check_view_impl(const rgb8c_view_t
& view
, const string
& name
)=0;
171 template <typename View
>
172 void check_view(const View
& img_view
, const string
& name
) {
173 rgb8_image_t
rgb_img(img_view
.dimensions());
174 copy_and_convert_pixels(img_view
,view(rgb_img
));
175 check_view_impl(const_view(rgb_img
), name
);
178 template <typename Img
> void basic_test(const string
& prefix
);
179 template <typename View
> void view_transformations_test(const View
& img_view
, const string
& prefix
);
180 template <typename View
> void homogeneous_view_transformations_test(const View
& img_view
, const string
& prefix
, std::true_type
);
181 template <typename View
> void homogeneous_view_transformations_test(const View
& img_view
, const string
& prefix
, std::false_type
)
183 boost::ignore_unused(img_view
);
184 boost::ignore_unused(prefix
);
186 template <typename View
> void histogram_test(const View
& img_view
, const string
& prefix
);
187 void virtual_view_test();
188 void packed_image_test();
189 void dynamic_image_test();
190 template <typename Img
> void image_all_test(const string
& prefix
);
193 // testing image iterators, clone, fill, locators, color convert
194 template <typename Img
>
195 void image_test::basic_test(const string
& prefix
) {
196 using View
= typename
Img::view_t
;
198 // make a 20x20 image
199 Img
img(typename
View::point_t(20,20));
200 const View
& img_view
=view(img
);
203 rgb8_pixel_t
red8(255,0,0), green8(0,255,0), blue8(0,0,255), white8(255,255,255);
204 typename
View::value_type red
,green
,blue
,white
;
205 color_convert(red8
,red
);
206 default_color_converter()(red8
,red
);
207 red
=color_convert_deref_fn
<rgb8_ref_t
,typename
Img::view_t::value_type
>()(red8
);
209 color_convert(green8
,green
);
210 color_convert(blue8
,blue
);
211 color_convert(white8
,white
);
212 fill(img_view
.begin(),img_view
.end(),red
);
214 color_convert(red8
,img_view
[0]);
216 // pointer to first pixel of second row
217 typename
View::reference rt
=img_view
.at(0,0)[img_view
.width()];
218 typename
View::x_iterator ptr
=&rt
;
219 typename
View::reference rt2
=*(img_view
.at(0,0)+img_view
.width());
220 typename
View::x_iterator ptr2
=&rt2
;
222 error_if(img_view
.x_at(0,0)+10!=10+img_view
.x_at(0,0));
224 // draw a blue line along the diagonal
225 typename
View::xy_locator loc
=img_view
.xy_at(0,img_view
.height()-1);
226 for (int y
=0; y
<img_view
.height(); ++y
) {
232 // draw a green dotted line along the main diagonal with step of 3
233 loc
=img_view
.xy_at(img_view
.width()-1,img_view
.height()-1);
234 while (loc
.x()>=img_view
.x_at(0,0)) {
236 loc
-=typename
View::point_t(3,3);
239 // Clone and make every red pixel white
241 for (typename
View::iterator it
=view(imgWhite
).end(); (it
-1)!=view(imgWhite
).begin(); --it
) {
246 check_view(img_view
,prefix
+"red_x");
247 check_view(view(imgWhite
),prefix
+"white_x");
250 template <typename View
>
251 void image_test::histogram_test(const View
& img_view
, const string
& prefix
) {
252 // vector<int> histogram(255,0);
253 // get_hist(cropped,histogram.begin());
254 unsigned char histogram
[256];
255 fill(histogram
,histogram
+256,0);
256 get_hist(img_view
,histogram
);
257 gray8c_view_t hist_view
=interleaved_view(256,1,(const gray8_pixel_t
*)histogram
,256);
258 check_view(hist_view
,prefix
+"histogram");
261 template <typename View
>
262 void image_test::view_transformations_test(const View
& img_view
, const string
& prefix
) {
263 check_view(img_view
,prefix
+"original");
265 check_view(subimage_view(img_view
, iround(img_view
.dimensions()/4), iround(img_view
.dimensions()/2)),prefix
+"cropped");
266 check_view(color_converted_view
<gray8_pixel_t
>(img_view
),prefix
+"gray8");
267 check_view(color_converted_view
<gray8_pixel_t
>(img_view
,my_color_converter()),prefix
+"my_gray8");
268 check_view(transposed_view(img_view
),prefix
+"transpose");
269 check_view(rotated180_view(img_view
),prefix
+"rot180");
270 check_view(rotated90cw_view(img_view
),prefix
+"90cw");
271 check_view(rotated90ccw_view(img_view
),prefix
+"90ccw");
272 check_view(flipped_up_down_view(img_view
),prefix
+"flipped_ud");
273 check_view(flipped_left_right_view(img_view
),prefix
+"flipped_lr");
274 check_view(subsampled_view(img_view
,typename
View::point_t(2,1)),prefix
+"subsampled");
275 check_view(kth_channel_view
<0>(img_view
),prefix
+"0th_k_channel");
276 homogeneous_view_transformations_test(img_view
, prefix
, view_is_homogeneous
<View
>());
279 template <typename View
>
280 void image_test::homogeneous_view_transformations_test(const View
& img_view
, const string
& prefix
, std::true_type
) {
281 check_view(nth_channel_view(img_view
,0),prefix
+"0th_n_channel");
284 void image_test::virtual_view_test()
286 using deref_t
= mandelbrot_fn
<rgb8_pixel_t
>;
287 using point_t
= deref_t::point_t
;
288 using locator_t
= virtual_2d_locator
<deref_t
, false>;
289 using my_virt_view_t
= image_view
<locator_t
>;
291 boost::function_requires
<PixelLocatorConcept
<locator_t
> >();
292 gil_function_requires
<StepIteratorConcept
<locator_t::x_iterator
> >();
294 point_t
dims(200,200);
295 my_virt_view_t
mandel(dims
, locator_t(point_t(0,0), point_t(1,1), deref_t(dims
, rgb8_pixel_t(255,0,255), rgb8_pixel_t(0,255,0))));
297 gray8s_image_t
img(dims
);
298 fill_pixels(view(img
),0); // our x_gradient algorithm doesn't change the first & last column, so make sure they are 0
299 x_gradient(color_converted_view
<gray8_pixel_t
>(mandel
), view(img
));
300 check_view(color_converted_view
<gray8_pixel_t
>(const_view(img
)), "mandelLuminosityGradient");
302 view_transformations_test(mandel
,"virtual_");
303 histogram_test(mandel
,"virtual_");
306 // Test alignment and packed images
307 void image_test::packed_image_test()
309 using bgr131_image_t
= bit_aligned_image3_type
<1,3,1, bgr_layout_t
>::type
;
310 using bgr131_pixel_t
= bgr131_image_t::value_type
;
311 bgr131_pixel_t
fill_val(1,3,1);
313 bgr131_image_t
bgr131_img(3,10);
314 fill_pixels(view(bgr131_img
), fill_val
);
316 bgr131_image_t
bgr131a_img(3,10,1);
317 copy_pixels(const_view(bgr131_img
), view(bgr131a_img
));
319 bgr131_image_t
bgr131b_img(3,10,4);
320 copy_pixels(const_view(bgr131_img
), view(bgr131b_img
));
322 error_if(bgr131_img
!=bgr131a_img
|| bgr131a_img
!=bgr131b_img
);
325 void image_test::dynamic_image_test()
327 using any_image_t
= any_image
338 rgb8_planar_image_t
img(sample_view
.dimensions());
339 copy_pixels(sample_view
, view(img
));
340 any_image_t any_img
=any_image_t(img
);
342 check_view(view(any_img
), "dynamic_");
343 check_view(flipped_left_right_view(view(any_img
)), "dynamic_fliplr");
344 check_view(flipped_up_down_view(view(any_img
)), "dynamic_flipud");
346 any_image_t::view_t subimageView
=subimage_view(view(any_img
),0,0,10,15);
348 check_view(subimageView
, "dynamic_subimage");
349 check_view(subsampled_view(rotated180_view(view(any_img
)), 2,1), "dynamic_subimage_subsampled180rot");
352 template <typename Img
>
353 void image_test::image_all_test(const string
& prefix
) {
354 basic_test
<Img
>(prefix
+"basic_");
357 img
.recreate(sample_view
.dimensions());
358 copy_and_convert_pixels(sample_view
,view(img
));
360 view_transformations_test(view(img
), prefix
+"views_");
362 histogram_test(const_view(img
),prefix
+"histogram_");
365 void image_test::run() {
368 image_all_test
<bgr8_image_t
>("bgr8_");
369 image_all_test
<rgb8_image_t
>("rgb8_");
370 image_all_test
<rgb8_planar_image_t
>("planarrgb8_");
371 image_all_test
<gray8_image_t
>("gray8_");
373 using bgr121_ref_t
= bit_aligned_pixel_reference
376 mp11::mp_list_c
<int, 1, 2, 1>,
380 using bgr121_image_t
= image
<bgr121_ref_t
, false>;
381 image_all_test
<bgr121_image_t
>("bgr121_");
384 view_transformations_test(subsampled_view(sample_view
, point_t(1,2)), "subsampled_");
385 view_transformations_test(color_converted_view
<gray8_pixel_t
>(sample_view
),"color_converted_");
389 dynamic_image_test();
394 ////////////////////////////////////////////////////
396 /// Performs or generates image tests using checksums
398 ////////////////////////////////////////////////////
400 class checksum_image_mgr
: public image_test
403 using crc_map_t
= map
<string
, boost::crc_32_type::value_type
>;
407 ////////////////////////////////////////////////////
409 /// Performs image tests by comparing image pixel checksums against a reference
411 ////////////////////////////////////////////////////
413 class checksum_image_test
: public checksum_image_mgr
{
415 checksum_image_test(const char* filename
) : _filename(filename
) {}
417 const char* _filename
;
418 void initialize() override
;
419 void check_view_impl(const rgb8c_view_t
& v
, const string
& name
) override
;
422 // Load the checksums from the reference file and create the start image
423 void checksum_image_test::initialize() {
424 boost::crc_32_type::value_type crc_result
;
425 fstream
checksum_ref(_filename
,ios::in
);
428 checksum_ref
>> crc_name
>> std::hex
>> crc_result
;
429 if(checksum_ref
.fail()) break;
430 if (!crc_name
.empty() && crc_name
[0] == '#')
432 crc_result
= 0; // skip test case
433 crc_name
= crc_name
.substr(1);
435 _crc_map
[crc_name
]=crc_result
;
437 checksum_ref
.close();
440 // Create a checksum for the given view and compare it with the reference checksum. Throw exception if different
441 void checksum_image_test::check_view_impl(const rgb8c_view_t
& img_view
, const string
& name
) {
442 boost::crc_32_type checksum_acumulator
;
443 checksum_acumulator
.process_bytes(img_view
.row_begin(0),img_view
.size()*3);
444 unsigned int const crc_expect
= _crc_map
[name
];
447 cerr
<< "Skipping checksum check for " << name
<< " (crc=0)" << endl
;
451 boost::crc_32_type::value_type
const crc
= checksum_acumulator
.checksum();
452 if (crc
==crc_expect
) {
453 cerr
<< "Checking checksum for " << name
<< " (crc=" << std::hex
<< crc
<< ")" << endl
;
456 cerr
<< "Checksum error in " << name
457 << " (crc=" << std::hex
<< crc
<< " != " << std::hex
<< crc_expect
<< ")" << endl
;
462 ////////////////////////////////////////////////////
464 /// Generates a set of reference checksums to compare against
466 ////////////////////////////////////////////////////
468 class checksum_image_generate
: public checksum_image_mgr
{
470 checksum_image_generate(const char* filename
) : _filename(filename
) {}
472 const char* _filename
;
473 void check_view_impl(const rgb8c_view_t
& img_view
, const string
& name
) override
;
474 void finalize() override
;
477 // Add the checksum of the given view to the map of checksums
478 void checksum_image_generate::check_view_impl(const rgb8c_view_t
& img_view
, const string
& name
) {
479 boost::crc_32_type result
;
480 result
.process_bytes(img_view
.row_begin(0),img_view
.size()*3);
481 cerr
<< "Generating checksum for " << name
<< endl
;
482 _crc_map
[name
] = result
.checksum();
485 // Save the checksums into the reference file
486 void checksum_image_generate::finalize() {
487 fstream
checksum_ref(_filename
,ios::out
);
488 for (crc_map_t::const_iterator it
=_crc_map
.begin(); it
!=_crc_map
.end(); ++it
) {
489 checksum_ref
<< it
->first
<< " " << std::hex
<< it
->second
<< "\r\n";
491 checksum_ref
.close();
494 ////////////////////////////////////////////////////
496 /// Performs or generates image tests using image I/O
498 ////////////////////////////////////////////////////
500 extern const string in_dir
;
501 extern const string out_dir
;
502 extern const string ref_dir
;
504 const string in_dir
=""; // directory of source images
505 const string out_dir
=in_dir
+"image-out/"; // directory where to write output
506 const string ref_dir
=in_dir
+"image-ref/"; // reference directory to compare written with actual output
508 void static_checks() {
509 gil_function_requires
<ImageConcept
<rgb8_image_t
> >();
511 static_assert(view_is_basic
<rgb8_step_view_t
>::value
, "");
512 static_assert(view_is_basic
<cmyk8c_planar_step_view_t
>::value
, "");
513 static_assert(view_is_basic
<rgb8_planar_view_t
>::value
, "");
515 static_assert(view_is_step_in_x
<rgb8_step_view_t
>::value
, "");
516 static_assert(view_is_step_in_x
<cmyk8c_planar_step_view_t
>::value
, "");
517 static_assert(!view_is_step_in_x
<rgb8_planar_view_t
>::value
, "");
519 static_assert(!is_planar
<rgb8_step_view_t
>::value
, "");
520 static_assert(is_planar
<cmyk8c_planar_step_view_t
>::value
, "");
521 static_assert(is_planar
<rgb8_planar_view_t
>::value
, "");
523 static_assert(view_is_mutable
<rgb8_step_view_t
>::value
, "");
524 static_assert(!view_is_mutable
<cmyk8c_planar_step_view_t
>::value
, "");
525 static_assert(view_is_mutable
<rgb8_planar_view_t
>::value
, "");
527 static_assert(std::is_same
529 derived_view_type
<cmyk8c_planar_step_view_t
>::type
,
530 cmyk8c_planar_step_view_t
532 static_assert(std::is_same
536 cmyk8c_planar_step_view_t
, std::uint16_t, rgb_layout_t
538 rgb16c_planar_step_view_t
540 static_assert(std::is_same
544 cmyk8c_planar_step_view_t
, use_default
, rgb_layout_t
, std::false_type
, use_default
, std::false_type
549 // test view get raw data (mostly compile-time test)
551 rgb8_image_t
rgb8(100,100);
552 unsigned char* data
=interleaved_view_get_raw_data(view(rgb8
));
553 const unsigned char* cdata
=interleaved_view_get_raw_data(const_view(rgb8
));
554 error_if(data
!=cdata
);
558 rgb16s_planar_image_t
rgb8(100,100);
559 short* data
=planar_view_get_raw_data(view(rgb8
),1);
560 const short* cdata
=planar_view_get_raw_data(const_view(rgb8
),1);
561 error_if(data
!=cdata
);
565 using image_test_t
= checksum_image_test
;
566 using image_generate_t
= checksum_image_generate
;
568 #ifdef BOOST_GIL_GENERATE_REFERENCE_DATA
569 using image_mgr_t
= image_generate_t
;
571 using image_mgr_t
= image_test_t
;
574 void test_image(const char* ref_checksum
) {
575 image_mgr_t
mgr(ref_checksum
);
577 cerr
<< "Reading checksums from " << ref_checksum
<< endl
;
582 int main(int argc
, char* argv
[])
587 throw std::runtime_error("No file with reference checksums specified");
589 std::string local_name
= argv
[1];
590 std::ifstream
file_is_there(local_name
.c_str());
592 throw std::runtime_error("Unable to open gil_reference_checksums.txt");
594 test_image(local_name
.c_str());
598 catch (std::exception
const& e
)
600 std::cerr
<< e
.what() << std::endl
;