/* Copyright 2005-2007 Adobe Systems Incorporated Use, modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). See http://opensource.adobe.com/gil for most recent version including documentation. */ // image_test.cpp : // #ifdef _MSC_VER //#pragma warning(disable : 4244) // conversion from 'gil::image::coord_t' to 'int', possible loss of data (visual studio compiler doesn't realize that the two types are the same) #pragma warning(disable : 4503) // decorated name length exceeded, name was truncated #endif #include #include #include #include #include #include #include #include #include #include #include using namespace boost::gil; using namespace std; using namespace boost; extern rgb8c_planar_view_t sample_view; void error_if(bool condition); // When BOOST_GIL_GENERATE_REFERENCE_DATA is defined, the reference data is generated and saved. // When it is undefined, regression tests are checked against it //#define BOOST_GIL_GENERATE_REFERENCE_DATA //////////////////////////////////////////////////// /// /// Some algorithms to use in testing /// //////////////////////////////////////////////////// template void gray_image_hist(const GrayView& img_view, R& hist) { // for_each_pixel(img_view,++lambda::var(hist)[lambda::_1]); for (typename GrayView::iterator it=img_view.begin(); it!=img_view.end(); ++it) ++hist[*it]; } template void get_hist(const V& img_view, R& hist) { gray_image_hist(color_converted_view(img_view), hist); } // testing custom color conversion template struct my_color_converter_impl : public default_color_converter_impl {}; template struct my_color_converter_impl { template void operator()(const P1& src, P2& dst) const { default_color_converter_impl()(src,dst); get_color(dst,gray_color_t())=channel_invert(get_color(dst,gray_color_t())); } }; struct my_color_converter { template void operator()(const SrcP& src,DstP& dst) const { typedef typename color_space_type::type src_cs_t; typedef typename color_space_type::type dst_cs_t; my_color_converter_impl()(src,dst); } }; // Models a Unary Function template // Models PixelValueConcept struct mandelbrot_fn { typedef point2 point_t; typedef mandelbrot_fn const_t; typedef P value_type; typedef value_type reference; typedef value_type const_reference; typedef point_t argument_type; typedef reference result_type; BOOST_STATIC_CONSTANT(bool, is_mutable=false); value_type _in_color,_out_color; point_t _img_size; static const int MAX_ITER=100; // max number of iterations mandelbrot_fn() {} 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) {} result_type operator()(const point_t& p) const { // normalize the coords to (-2..1, -1.5..1.5) // (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods) double t=get_num_iter(point2(p.x/(double)_img_size.x*3-2, p.y/(double)_img_size.y*3-1.0f));//1.5f)); t=pow(t,0.2); value_type ret; for (int k=0; k::value; ++k) ret[k]=(typename channel_type::type)(_in_color[k]*t + _out_color[k]*(1-t)); return ret; } private: double get_num_iter(const point2& p) const { point2 Z(0,0); for (int i=0; i(Z.x*Z.x - Z.y*Z.y + p.x, 2*Z.x*Z.y + p.y); if (Z.x*Z.x + Z.y*Z.y > 4) return i/(double)MAX_ITER; } return 0; } }; template void x_gradient(const T& src, const gray8s_view_t& dst) { for (int y=0; y struct pixel_is_homogeneous : public mpl::true_ {}; template struct pixel_is_homogeneous > : public mpl::false_ {}; template struct view_is_homogeneous : public pixel_is_homogeneous {}; //////////////////////////////////////////////////// /// /// Tests image view transformations and algorithms /// //////////////////////////////////////////////////// class image_test { public: virtual void initialize() {} virtual void finalize() {} virtual ~image_test() {} void run(); protected: virtual void check_view_impl(const rgb8c_view_t& view, const string& name)=0; template void check_view(const View& img_view, const string& name) { rgb8_image_t rgb_img(img_view.dimensions()); copy_and_convert_pixels(img_view,view(rgb_img)); check_view_impl(const_view(rgb_img), name); } private: template void basic_test(const string& prefix); template void view_transformations_test(const View& img_view, const string& prefix); template void homogeneous_view_transformations_test(const View& img_view, const string& prefix, mpl::true_); template void homogeneous_view_transformations_test(const View& img_view, const string& prefix, mpl::false_) {} template void histogram_test(const View& img_view, const string& prefix); void virtual_view_test(); void packed_image_test(); void dynamic_image_test(); template void image_all_test(const string& prefix); }; // testing image iterators, clone, fill, locators, color convert template void image_test::basic_test(const string& prefix) { typedef typename Img::view_t View; // make a 20x20 image Img img(typename View::point_t(20,20)); const View& img_view=view(img); // fill it with red rgb8_pixel_t red8(255,0,0), green8(0,255,0), blue8(0,0,255), white8(255,255,255); typename View::value_type red,green,blue,white; color_convert(red8,red); default_color_converter()(red8,red); red=color_convert_deref_fn()(red8); color_convert(green8,green); color_convert(blue8,blue); color_convert(white8,white); fill(img_view.begin(),img_view.end(),red); color_convert(red8,img_view[0]); // pointer to first pixel of second row typename View::reference rt=img_view.at(0,0)[img_view.width()]; typename View::x_iterator ptr=&rt; typename View::reference rt2=*(img_view.at(0,0)+img_view.width()); typename View::x_iterator ptr2=&rt2; error_if(ptr!=ptr2); error_if(img_view.x_at(0,0)+10!=10+img_view.x_at(0,0)); // draw a blue line along the diagonal typename View::xy_locator loc=img_view.xy_at(0,img_view.height()-1); for (int y=0; y=img_view.x_at(0,0)) { *loc=green; loc-=typename View::point_t(3,3); } // Clone and make every red pixel white Img imgWhite(img); for (typename View::iterator it=view(imgWhite).end(); (it-1)!=view(imgWhite).begin(); --it) { if (*(it-1)==red) *(it-1)=white; } check_view(img_view,prefix+"red_x"); check_view(view(imgWhite),prefix+"white_x"); } template void image_test::histogram_test(const View& img_view, const string& prefix) { // vector histogram(255,0); // get_hist(cropped,histogram.begin()); unsigned char histogram[256]; fill(histogram,histogram+256,0); get_hist(img_view,histogram); gray8c_view_t hist_view=interleaved_view(256,1,(const gray8_pixel_t*)histogram,256); check_view(hist_view,prefix+"histogram"); } template void image_test::view_transformations_test(const View& img_view, const string& prefix) { check_view(img_view,prefix+"original"); check_view(subimage_view(img_view, iround(img_view.dimensions()/4), iround(img_view.dimensions()/2)),prefix+"cropped"); check_view(color_converted_view(img_view),prefix+"gray8"); check_view(color_converted_view(img_view,my_color_converter()),prefix+"my_gray8"); check_view(transposed_view(img_view),prefix+"transpose"); check_view(rotated180_view(img_view),prefix+"rot180"); check_view(rotated90cw_view(img_view),prefix+"90cw"); check_view(rotated90ccw_view(img_view),prefix+"90ccw"); check_view(flipped_up_down_view(img_view),prefix+"flipped_ud"); check_view(flipped_left_right_view(img_view),prefix+"flipped_lr"); check_view(subsampled_view(img_view,typename View::point_t(2,1)),prefix+"subsampled"); check_view(kth_channel_view<0>(img_view),prefix+"0th_k_channel"); homogeneous_view_transformations_test(img_view, prefix, view_is_homogeneous()); } template void image_test::homogeneous_view_transformations_test(const View& img_view, const string& prefix, mpl::true_) { check_view(nth_channel_view(img_view,0),prefix+"0th_n_channel"); } void image_test::virtual_view_test() { typedef mandelbrot_fn deref_t; typedef deref_t::point_t point_t; typedef virtual_2d_locator locator_t; typedef image_view my_virt_view_t; boost::function_requires >(); gil_function_requires >(); point_t dims(200,200); 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)))); gray8s_image_t img(dims); fill_pixels(view(img),0); // our x_gradient algorithm doesn't change the first & last column, so make sure they are 0 x_gradient(color_converted_view(mandel), view(img)); check_view(color_converted_view(const_view(img)), "mandelLuminosityGradient"); view_transformations_test(mandel,"virtual_"); histogram_test(mandel,"virtual_"); } // Test alignment and packed images void image_test::packed_image_test() { typedef bit_aligned_image3_type<1,3,1, bgr_layout_t>::type bgr131_image_t; typedef bgr131_image_t::value_type bgr131_pixel_t; bgr131_pixel_t fill_val(1,3,1); bgr131_image_t bgr131_img(3,10); fill_pixels(view(bgr131_img), fill_val); bgr131_image_t bgr131a_img(3,10,1); copy_pixels(const_view(bgr131_img), view(bgr131a_img)); bgr131_image_t bgr131b_img(3,10,4); copy_pixels(const_view(bgr131_img), view(bgr131b_img)); error_if(bgr131_img!=bgr131a_img || bgr131a_img!=bgr131b_img); } void image_test::dynamic_image_test() { typedef any_image > any_image_t; rgb8_planar_image_t img(sample_view.dimensions()); copy_pixels(sample_view, view(img)); any_image_t any_img=any_image_t(img); check_view(view(any_img), "dynamic_"); check_view(flipped_left_right_view(view(any_img)), "dynamic_fliplr"); check_view(flipped_up_down_view(view(any_img)), "dynamic_flipud"); any_image_t::view_t subimageView=subimage_view(view(any_img),0,0,10,15); check_view(subimageView, "dynamic_subimage"); check_view(subsampled_view(rotated180_view(view(any_img)), 2,1), "dynamic_subimage_subsampled180rot"); } template void image_test::image_all_test(const string& prefix) { basic_test(prefix+"basic_"); Img img; img.recreate(sample_view.dimensions()); copy_and_convert_pixels(sample_view,view(img)); view_transformations_test(view(img), prefix+"views_"); histogram_test(const_view(img),prefix+"histogram_"); } void image_test::run() { initialize(); image_all_test("bgr8_"); image_all_test("rgb8_"); image_all_test("planarrgb8_"); image_all_test("gray8_"); typedef const bit_aligned_pixel_reference, bgr_layout_t, true> bgr121_ref_t; typedef image bgr121_image_t; image_all_test("bgr121_"); // TODO: Remove? view_transformations_test(subsampled_view(sample_view,point2(1,2)),"subsampled_"); view_transformations_test(color_converted_view(sample_view),"color_converted_"); virtual_view_test(); packed_image_test(); dynamic_image_test(); finalize(); } //////////////////////////////////////////////////// /// /// Performs or generates image tests using checksums /// //////////////////////////////////////////////////// class checksum_image_mgr : public image_test { protected: typedef map crc_map_t; crc_map_t _crc_map; }; //////////////////////////////////////////////////// /// /// Performs image tests by comparing image pixel checksums against a reference /// //////////////////////////////////////////////////// class checksum_image_test : public checksum_image_mgr { public: checksum_image_test(const char* filename) : _filename(filename) {} private: const char* _filename; virtual void initialize(); virtual void check_view_impl(const rgb8c_view_t& v, const string& name); }; // Load the checksums from the reference file and create the start image void checksum_image_test::initialize() { string crc_name; boost::crc_32_type::value_type crc_result; fstream checksum_ref(_filename,ios::in); while (true) { checksum_ref >> crc_name >> std::hex >> crc_result; if(checksum_ref.fail()) break; _crc_map[crc_name]=crc_result; } checksum_ref.close(); } // Create a checksum for the given view and compare it with the reference checksum. Throw exception if different void checksum_image_test::check_view_impl(const rgb8c_view_t& img_view, const string& name) { boost::crc_32_type checksum_acumulator; checksum_acumulator.process_bytes(img_view.row_begin(0),img_view.size()*3); cerr << "Checking checksum for " << name << endl; if (checksum_acumulator.checksum()!=_crc_map[name]) { cerr << "Checksum error in "<first << " " << std::hex << it->second << "\r\n"; } checksum_ref.close(); } //////////////////////////////////////////////////// /// /// Performs or generates image tests using image I/O /// //////////////////////////////////////////////////// extern const string in_dir; extern const string out_dir; extern const string ref_dir; const string in_dir=""; // directory of source images const string out_dir=in_dir+"image-out/"; // directory where to write output const string ref_dir=in_dir+"image-ref/"; // reference directory to compare written with actual output #ifndef BOOST_GIL_NO_IO #include class file_image_mgr : public image_test {}; class file_image_test : public file_image_mgr { public: file_image_test(const char*) {} protected: void check_view_impl(const boost::gil::rgb8c_view_t& img_view,const string& name) { jpeg_write_view(out_dir+name+".jpg",img_view); rgb8_image_t img1, img2; jpeg_read_and_convert_image(out_dir+name+".jpg",img1); cerr << "Testing "< >(); BOOST_STATIC_ASSERT(view_is_basic::value); BOOST_STATIC_ASSERT(view_is_basic::value); BOOST_STATIC_ASSERT(view_is_basic::value); BOOST_STATIC_ASSERT(view_is_step_in_x::value); BOOST_STATIC_ASSERT(view_is_step_in_x::value); BOOST_STATIC_ASSERT(!view_is_step_in_x::value); BOOST_STATIC_ASSERT(!is_planar::value); BOOST_STATIC_ASSERT(is_planar::value); BOOST_STATIC_ASSERT(is_planar::value); BOOST_STATIC_ASSERT(view_is_mutable::value); BOOST_STATIC_ASSERT(!view_is_mutable::value); BOOST_STATIC_ASSERT(view_is_mutable::value); BOOST_STATIC_ASSERT((boost::is_same::type, cmyk8c_planar_step_view_t>::value)); BOOST_STATIC_ASSERT((boost::is_same::type, rgb16c_planar_step_view_t>::value)); BOOST_STATIC_ASSERT((boost::is_same::type, rgb8c_step_view_t>::value)); // test view get raw data (mostly compile-time test) { rgb8_image_t rgb8(100,100); unsigned char* data=interleaved_view_get_raw_data(view(rgb8)); const unsigned char* cdata=interleaved_view_get_raw_data(const_view(rgb8)); error_if(data!=cdata); } { rgb16s_planar_image_t rgb8(100,100); short* data=planar_view_get_raw_data(view(rgb8),1); const short* cdata=planar_view_get_raw_data(const_view(rgb8),1); error_if(data!=cdata); } } #ifdef BOOST_GIL_NO_IO typedef checksum_image_test image_test_t; typedef checksum_image_generate image_generate_t; #else typedef file_image_test image_test_t; typedef file_image_generate image_generate_t; #endif #ifdef BOOST_GIL_GENERATE_REFERENCE_DATA typedef image_generate_t image_mgr_t; #else typedef image_test_t image_mgr_t; #endif void test_image(const char* ref_checksum) { image_mgr_t mgr(ref_checksum); mgr.run(); static_checks(); } int main(int argc, char* argv[]) { const char* local_name = "gil_reference_checksums.txt"; const char* name_from_status = "../libs/gil/test/gil_reference_checksums.txt"; std::ifstream file_is_there(local_name); if (file_is_there) { test_image(local_name); } else { std::ifstream file_is_there(name_from_status); if (file_is_there) test_image(name_from_status); else { std::cerr << "Unable to open gil_reference_checksums.txt"<