diff options
Diffstat (limited to 'ml/dlib/dlib/image_processing')
35 files changed, 0 insertions, 14828 deletions
diff --git a/ml/dlib/dlib/image_processing/box_overlap_testing.h b/ml/dlib/dlib/image_processing/box_overlap_testing.h deleted file mode 100644 index 32409d13e..000000000 --- a/ml/dlib/dlib/image_processing/box_overlap_testing.h +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_BOX_OVERlAP_TESTING_Hh_ -#define DLIB_BOX_OVERlAP_TESTING_Hh_ - -#include "box_overlap_testing_abstract.h" -#include "../geometry.h" -#include <vector> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - inline double box_intersection_over_union ( - const drectangle& a, - const drectangle& b - ) - { - const double inner = a.intersect(b).area(); - if (inner == 0) - return 0; - const double outer = (a+b).area(); - return inner/outer; - } - -// ---------------------------------------------------------------------------------------- - - inline double box_intersection_over_union ( - const rectangle& a, - const rectangle& b - ) - { - return box_intersection_over_union(drectangle(a),drectangle(b)); - } - -// ---------------------------------------------------------------------------------------- - - inline double box_percent_covered ( - const drectangle& a, - const drectangle& b - ) - { - const double inner = a.intersect(b).area(); - if (inner == 0) - return 0; - return std::max(inner/a.area(), inner/b.area()); - } - -// ---------------------------------------------------------------------------------------- - - inline double box_percent_covered ( - const rectangle& a, - const rectangle& b - ) - { - return box_percent_covered(drectangle(a), drectangle(b)); - } - -// ---------------------------------------------------------------------------------------- - - class test_box_overlap - { - public: - test_box_overlap ( - ) : iou_thresh(0.5), percent_covered_thresh(1.0) - {} - - explicit test_box_overlap ( - double iou_thresh_, - double percent_covered_thresh_ = 1.0 - ) : iou_thresh(iou_thresh_), percent_covered_thresh(percent_covered_thresh_) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 <= iou_thresh && iou_thresh <= 1 && - 0 <= percent_covered_thresh && percent_covered_thresh <= 1, - "\t test_box_overlap::test_box_overlap(iou_thresh, percent_covered_thresh)" - << "\n\t Invalid inputs were given to this function " - << "\n\t iou_thresh: " << iou_thresh - << "\n\t percent_covered_thresh: " << percent_covered_thresh - << "\n\t this: " << this - ); - - } - - bool operator() ( - const dlib::rectangle& a, - const dlib::rectangle& b - ) const - { - const double inner = a.intersect(b).area(); - if (inner == 0) - return false; - - const double outer = (a+b).area(); - if (inner/outer > iou_thresh || - inner/a.area() > percent_covered_thresh || - inner/b.area() > percent_covered_thresh) - return true; - else - return false; - } - - double get_percent_covered_thresh ( - ) const - { - return percent_covered_thresh; - } - - double get_iou_thresh ( - ) const - { - return iou_thresh; - } - - private: - double iou_thresh; - double percent_covered_thresh; - }; - -// ---------------------------------------------------------------------------------------- - - inline void serialize ( - const test_box_overlap& item, - std::ostream& out - ) - { - serialize(item.get_iou_thresh(), out); - serialize(item.get_percent_covered_thresh(), out); - } - - inline void deserialize ( - test_box_overlap& item, - std::istream& in - ) - { - double percent_covered_thresh, iou_thresh; - deserialize(iou_thresh, in); - deserialize(percent_covered_thresh, in); - item = test_box_overlap(iou_thresh, percent_covered_thresh); - } - -// ---------------------------------------------------------------------------------------- - - inline test_box_overlap find_tight_overlap_tester ( - const std::vector<std::vector<rectangle> >& rects - ) - { - double max_pcov = 0; - double max_iou_score = 0; - for (unsigned long i = 0; i < rects.size(); ++i) - { - for (unsigned long j = 0; j < rects[i].size(); ++j) - { - for (unsigned long k = j+1; k < rects[i].size(); ++k) - { - const rectangle a = rects[i][j]; - const rectangle b = rects[i][k]; - const double iou_score = (a.intersect(b)).area()/(double)(a+b).area(); - const double pcov_a = (a.intersect(b)).area()/(double)(a).area(); - const double pcov_b = (a.intersect(b)).area()/(double)(b).area(); - - if (iou_score > max_iou_score) - max_iou_score = iou_score; - - if (pcov_a > max_pcov) - max_pcov = pcov_a; - if (pcov_b > max_pcov) - max_pcov = pcov_b; - } - } - } - - // Relax these thresholds very slightly. We do this because on some systems the - // boxes that generated the max values erroneously trigger a box overlap iou even - // though their percent covered and iou values are *equal* to the thresholds but - // not greater. That is, sometimes when double values get moved around they change - // their values slightly, so this avoids the problems that can create. - max_iou_score = std::min(1.0000001*max_iou_score, 1.0); - max_pcov = std::min(1.0000001*max_pcov, 1.0); - return test_box_overlap(max_iou_score, max_pcov); - } - -// ---------------------------------------------------------------------------------------- - - inline bool overlaps_any_box ( - const test_box_overlap& tester, - const std::vector<rectangle>& rects, - const rectangle& rect - ) - { - for (unsigned long i = 0; i < rects.size(); ++i) - { - if (tester(rects[i],rect)) - return true; - } - return false; - } - -// ---------------------------------------------------------------------------------------- - - inline bool overlaps_any_box ( - const std::vector<rectangle>& rects, - const rectangle& rect - ) - { - return overlaps_any_box(test_box_overlap(),rects,rect); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_BOX_OVERlAP_TESTING_Hh_ - diff --git a/ml/dlib/dlib/image_processing/box_overlap_testing_abstract.h b/ml/dlib/dlib/image_processing/box_overlap_testing_abstract.h deleted file mode 100644 index 1bb4a28ae..000000000 --- a/ml/dlib/dlib/image_processing/box_overlap_testing_abstract.h +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_BOX_OVERlAP_TESTING_ABSTRACT_Hh_ -#ifdef DLIB_BOX_OVERlAP_TESTING_ABSTRACT_Hh_ - -#include "../geometry.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - inline double box_intersection_over_union ( - const drectangle& a, - const drectangle& b - ); - /*! - ensures - - returns area of the intersection of a and b divided by (a+b).area(). If both - boxes are empty then returns 0. - !*/ - -// ---------------------------------------------------------------------------------------- - - inline double box_intersection_over_union ( - const rectangle& a, - const rectangle& b - ); - /*! - ensures - - returns area of the intersection of a and b divided by (a+b).area(). If both - boxes are empty then returns 0. - !*/ - -// ---------------------------------------------------------------------------------------- - - inline double box_percent_covered ( - const drectangle& a, - const drectangle& b - ); - /*! - ensures - - let OVERLAP = a.intersect(b).area() - - This function returns max(OVERLAP/a.area(), OVERLAP/b.area()) - e.g. If one box entirely contains another then this function returns 1, if - they don't overlap at all it returns 0. - !*/ - -// ---------------------------------------------------------------------------------------- - - inline double box_percent_covered ( - const rectangle& a, - const rectangle& b - ); - /*! - ensures - - let OVERLAP = a.intersect(b).area() - - This function returns max(OVERLAP/a.area(), OVERLAP/b.area()) - e.g. If one box entirely contains another then this function returns 1, if - they don't overlap at all it returns 0. - !*/ - -// ---------------------------------------------------------------------------------------- - - class test_box_overlap - { - /*! - WHAT THIS OBJECT REPRESENTS - This object is a simple function object for determining if two rectangles - overlap. - - THREAD SAFETY - Concurrent access to an instance of this object is safe provided that - only const member functions are invoked. Otherwise, access must be - protected by a mutex lock. - !*/ - - public: - test_box_overlap ( - ); - /*! - ensures - - #get_iou_thresh() == 0.5 - - #get_percent_covered_thresh() == 1.0 - !*/ - - explicit test_box_overlap ( - double iou_thresh, - double percent_covered_thresh = 1.0 - ); - /*! - requires - - 0 <= iou_thresh <= 1 - - 0 <= percent_covered_thresh <= 1 - ensures - - #get_iou_thresh() == iou_thresh - - #get_percent_covered_thresh() == percent_covered_thresh - !*/ - - bool operator() ( - const dlib::rectangle& a, - const dlib::rectangle& b - ) const; - /*! - ensures - - returns true if a and b overlap "enough". This is defined precisely below. - - if (a.intersect(b).area()/(a+b).area() > get_iou_thresh() || - a.intersect(b).area()/a.area() > get_percent_covered_thresh() || - a.intersect(b).area()/b.area() > get_percent_covered_thresh() ) then - - returns true - - else - - returns false - !*/ - - double get_iou_thresh ( - ) const; - /*! - ensures - - returns the threshold used to determine if two rectangle's intersection - over union value is big enough to be considered a match. Note that the - iou score varies from 0 to 1 and only becomes 1 when two rectangles are - identical. - !*/ - - double get_percent_covered_thresh ( - ) const; - /*! - ensures - - returns the threshold used to determine if two rectangles overlap. This - value is the percent of a rectangle's area covered by another rectangle. - !*/ - - }; - -// ---------------------------------------------------------------------------------------- - - void serialize ( - const test_box_overlap& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - void deserialize ( - test_box_overlap& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - - test_box_overlap find_tight_overlap_tester ( - const std::vector<std::vector<rectangle> >& rects - ); - /*! - ensures - - This function finds the most restrictive test_box_overlap object possible - that is consistent with the given set of sets of rectangles. - - To be precise, this function finds and returns a test_box_overlap object - TBO such that: - - TBO.get_iou_thresh() and TBO.get_percent_covered_thresh() are as small - as possible such that the following conditions are satisfied. - - for all valid i: - - for all distinct rectangles A and B in rects[i]: - - TBO(A,B) == false - !*/ - -// ---------------------------------------------------------------------------------------- - - bool overlaps_any_box ( - const test_box_overlap& tester, - const std::vector<rectangle>& rects, - const rectangle& rect - ); - /*! - ensures - - returns true if rect overlaps any box in rects and false otherwise. Overlap - is determined based on the given tester object. - !*/ - -// ---------------------------------------------------------------------------------------- - - bool overlaps_any_box ( - const std::vector<rectangle>& rects, - const rectangle& rect - ); - /*! - ensures - - returns overlaps_any_box(test_box_overlap(), rects, rect) - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_BOX_OVERlAP_TESTING_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/correlation_tracker.h b/ml/dlib/dlib/image_processing/correlation_tracker.h deleted file mode 100644 index f005ddc7b..000000000 --- a/ml/dlib/dlib/image_processing/correlation_tracker.h +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (C) 2015 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_CORRELATION_TrACKER_H_ -#define DLIB_CORRELATION_TrACKER_H_ - -#include "correlation_tracker_abstract.h" -#include "../geometry.h" -#include "../matrix.h" -#include "../array2d.h" -#include "../image_transforms/assign_image.h" -#include "../image_transforms/interpolation.h" - - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class correlation_tracker - { - public: - - explicit correlation_tracker (unsigned long filter_size = 6, - unsigned long num_scale_levels = 5, - unsigned long scale_window_size = 23, - double regularizer_space = 0.001, - double nu_space = 0.025, - double regularizer_scale = 0.001, - double nu_scale = 0.025, - double scale_pyramid_alpha = 1.020 - ) - : filter_size(1 << filter_size), num_scale_levels(1 << num_scale_levels), - scale_window_size(scale_window_size), - regularizer_space(regularizer_space), nu_space(nu_space), - regularizer_scale(regularizer_scale), nu_scale(nu_scale), - scale_pyramid_alpha(scale_pyramid_alpha) - { - // Create the cosine mask used for space filtering. - mask = make_cosine_mask(); - - // Create the cosine mask used for the scale filtering. - scale_cos_mask.resize(get_num_scale_levels()); - const long max_level = get_num_scale_levels()/2; - for (unsigned long k = 0; k < get_num_scale_levels(); ++k) - { - double dist = std::abs((double)k-max_level)/max_level*pi/2; - dist = std::min(dist, pi/2); - scale_cos_mask[k] = std::cos(dist); - } - } - - template <typename image_type> - void start_track ( - const image_type& img, - const drectangle& p - ) - { - DLIB_CASSERT(p.is_empty() == false, - "\t void correlation_tracker::start_track()" - << "\n\t You can't give an empty rectangle." - ); - - B.set_size(0,0); - - point_transform_affine tform = inv(make_chip(img, p, F)); - for (unsigned long i = 0; i < F.size(); ++i) - fft_inplace(F[i]); - make_target_location_image(tform(center(p)), G); - A.resize(F.size()); - for (unsigned long i = 0; i < F.size(); ++i) - { - A[i] = pointwise_multiply(G, F[i]); - B += squared(real(F[i]))+squared(imag(F[i])); - } - - position = p; - - // now do the scale space stuff - make_scale_space(img, Fs); - for (unsigned long i = 0; i < Fs.size(); ++i) - fft_inplace(Fs[i]); - make_scale_target_location_image(get_num_scale_levels()/2, Gs); - Bs.set_size(0); - As.resize(Fs.size()); - for (unsigned long i = 0; i < Fs.size(); ++i) - { - As[i] = pointwise_multiply(Gs, Fs[i]); - Bs += squared(real(Fs[i]))+squared(imag(Fs[i])); - } - } - - - unsigned long get_filter_size ( - ) const { return filter_size; } - - unsigned long get_num_scale_levels( - ) const { return num_scale_levels; } - - unsigned long get_scale_window_size ( - ) const { return scale_window_size; } - - double get_regularizer_space ( - ) const { return regularizer_space; } - inline double get_nu_space ( - ) const { return nu_space;} - - double get_regularizer_scale ( - ) const { return regularizer_scale; } - double get_nu_scale ( - ) const { return nu_scale;} - - drectangle get_position ( - ) const - { - return position; - } - - double get_scale_pyramid_alpha ( - ) const { return scale_pyramid_alpha; } - - - template <typename image_type> - double update_noscale( - const image_type& img, - const drectangle& guess - ) - { - DLIB_CASSERT(get_position().is_empty() == false, - "\t double correlation_tracker::update()" - << "\n\t You must call start_track() first before calling update()." - ); - - - const point_transform_affine tform = make_chip(img, guess, F); - for (unsigned long i = 0; i < F.size(); ++i) - fft_inplace(F[i]); - - // use the current filter to predict the object's location - G = 0; - for (unsigned long i = 0; i < F.size(); ++i) - G += pointwise_multiply(F[i],conj(A[i])); - G = pointwise_multiply(G, reciprocal(B+get_regularizer_space())); - ifft_inplace(G); - const dlib::vector<double,2> pp = max_point_interpolated(real(G)); - - - // Compute the peak to side lobe ratio. - const point p = pp; - running_stats<double> rs; - const rectangle peak = centered_rect(p, 8,8); - for (long r = 0; r < G.nr(); ++r) - { - for (long c = 0; c < G.nc(); ++c) - { - if (!peak.contains(point(c,r))) - rs.add(G(r,c).real()); - } - } - const double psr = (G(p.y(),p.x()).real()-rs.mean())/rs.stddev(); - - // update the position of the object - position = translate_rect(guess, tform(pp)-center(guess)); - - // now update the position filters - make_target_location_image(pp, G); - B *= (1-get_nu_space()); - for (unsigned long i = 0; i < F.size(); ++i) - { - A[i] = get_nu_space()*pointwise_multiply(G, F[i]) + (1-get_nu_space())*A[i]; - B += get_nu_space()*(squared(real(F[i]))+squared(imag(F[i]))); - } - - return psr; - } - - template <typename image_type> - double update ( - const image_type& img, - const drectangle& guess - ) - { - double psr = update_noscale(img, guess); - - // Now predict the scale change - make_scale_space(img, Fs); - for (unsigned long i = 0; i < Fs.size(); ++i) - fft_inplace(Fs[i]); - Gs = 0; - for (unsigned long i = 0; i < Fs.size(); ++i) - Gs += pointwise_multiply(Fs[i],conj(As[i])); - Gs = pointwise_multiply(Gs, reciprocal(Bs+get_regularizer_scale())); - ifft_inplace(Gs); - const double pos = max_point_interpolated(real(Gs)).y(); - - // update the rectangle's scale - position *= std::pow(get_scale_pyramid_alpha(), pos-(double)get_num_scale_levels()/2); - - - - // Now update the scale filters - make_scale_target_location_image(pos, Gs); - Bs *= (1-get_nu_scale()); - for (unsigned long i = 0; i < Fs.size(); ++i) - { - As[i] = get_nu_scale()*pointwise_multiply(Gs, Fs[i]) + (1-get_nu_scale())*As[i]; - Bs += get_nu_scale()*(squared(real(Fs[i]))+squared(imag(Fs[i]))); - } - - - return psr; - } - - template <typename image_type> - double update_noscale ( - const image_type& img - ) - { - return update_noscale(img, get_position()); - } - - template <typename image_type> - double update( - const image_type& img - ) - { - return update(img, get_position()); - } - - private: - - template <typename image_type> - void make_scale_space( - const image_type& img, - std::vector<matrix<std::complex<double>,0,1> >& Fs - ) const - { - typedef typename image_traits<image_type>::pixel_type pixel_type; - - // Make an image pyramid and put it into the chips array. - const long chip_size = get_scale_window_size(); - drectangle ppp = position*std::pow(get_scale_pyramid_alpha(), -(double)get_num_scale_levels()/2); - dlib::array<array2d<pixel_type> > chips; - std::vector<dlib::vector<double,2> > from_points, to_points; - from_points.push_back(point(0,0)); - from_points.push_back(point(chip_size-1,0)); - from_points.push_back(point(chip_size-1,chip_size-1)); - for (unsigned long i = 0; i < get_num_scale_levels(); ++i) - { - array2d<pixel_type> chip(chip_size,chip_size); - - // pull box into chip - to_points.clear(); - to_points.push_back(ppp.tl_corner()); - to_points.push_back(ppp.tr_corner()); - to_points.push_back(ppp.br_corner()); - transform_image(img,chip,interpolate_bilinear(),find_affine_transform(from_points, to_points)); - - chips.push_back(chip); - ppp *= get_scale_pyramid_alpha(); - } - - - // extract HOG for each chip - dlib::array<dlib::array<array2d<float> > > hogs(chips.size()); - for (unsigned long i = 0; i < chips.size(); ++i) - { - extract_fhog_features(chips[i], hogs[i], 4); - hogs[i].resize(32); - assign_image(hogs[i][31], chips[i]); - assign_image(hogs[i][31], mat(hogs[i][31])/255.0); - } - - // Now copy the hog features into the Fs outputs and also apply the cosine - // windowing. - Fs.resize(hogs[0].size()*hogs[0][0].size()); - unsigned long i = 0; - for (long r = 0; r < hogs[0][0].nr(); ++r) - { - for (long c = 0; c < hogs[0][0].nc(); ++c) - { - for (unsigned long j = 0; j < hogs[0].size(); ++j) - { - Fs[i].set_size(hogs.size()); - for (unsigned long k = 0; k < hogs.size(); ++k) - { - Fs[i](k) = hogs[k][j][r][c]*scale_cos_mask[k]; - } - ++i; - } - } - } - } - - template <typename image_type> - point_transform_affine make_chip ( - const image_type& img, - drectangle p, - std::vector<matrix<std::complex<double> > >& chip - ) const - { - typedef typename image_traits<image_type>::pixel_type pixel_type; - array2d<pixel_type> temp; - const double padding = 1.4; - const chip_details details(p*padding, chip_dims(get_filter_size(), get_filter_size())); - extract_image_chip(img, details, temp); - - - chip.resize(32); - dlib::array<array2d<float> > hog; - extract_fhog_features(temp, hog, 1, 3,3 ); - for (unsigned long i = 0; i < hog.size(); ++i) - assign_image(chip[i], pointwise_multiply(matrix_cast<double>(mat(hog[i])), mask)); - - assign_image(chip[31], temp); - assign_image(chip[31], pointwise_multiply(mat(chip[31]), mask)/255.0); - - return inv(get_mapping_to_chip(details)); - } - - void make_target_location_image ( - const dlib::vector<double,2>& p, - matrix<std::complex<double> >& g - ) const - { - g.set_size(get_filter_size(), get_filter_size()); - g = 0; - rectangle area = centered_rect(p, 21,21).intersect(get_rect(g)); - for (long r = area.top(); r <= area.bottom(); ++r) - { - for (long c = area.left(); c <= area.right(); ++c) - { - double dist = length(point(c,r)-p); - g(r,c) = std::exp(-dist/3.0); - } - } - fft_inplace(g); - g = conj(g); - } - - - void make_scale_target_location_image ( - const double scale, - matrix<std::complex<double>,0,1>& g - ) const - { - g.set_size(get_num_scale_levels()); - for (long i = 0; i < g.size(); ++i) - { - double dist = std::pow((i-scale),2.0); - g(i) = std::exp(-dist/1.000); - } - fft_inplace(g); - g = conj(g); - } - - matrix<double> make_cosine_mask ( - ) const - { - const long size = get_filter_size(); - matrix<double> temp(size,size); - point cent = center(get_rect(temp)); - for (long r = 0; r < temp.nr(); ++r) - { - for (long c = 0; c < temp.nc(); ++c) - { - point delta = point(c,r)-cent; - double dist = length(delta)/(size/2.0)*(pi/2); - dist = std::min(dist*1.0, pi/2); - - temp(r,c) = std::cos(dist); - } - } - return temp; - } - - - std::vector<matrix<std::complex<double> > > A, F; - matrix<double> B; - - std::vector<matrix<std::complex<double>,0,1> > As, Fs; - matrix<double,0,1> Bs; - drectangle position; - - matrix<double> mask; - std::vector<double> scale_cos_mask; - - // G and Gs do not logically contribute to the state of this object. They are - // here just so we can void reallocating them over and over. - matrix<std::complex<double> > G; - matrix<std::complex<double>,0,1> Gs; - - unsigned long filter_size; - unsigned long num_scale_levels; - unsigned long scale_window_size; - double regularizer_space; - double nu_space; - double regularizer_scale; - double nu_scale; - double scale_pyramid_alpha; - }; -} - -#endif // DLIB_CORRELATION_TrACKER_H_ - diff --git a/ml/dlib/dlib/image_processing/correlation_tracker_abstract.h b/ml/dlib/dlib/image_processing/correlation_tracker_abstract.h deleted file mode 100644 index 5514f5e76..000000000 --- a/ml/dlib/dlib/image_processing/correlation_tracker_abstract.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (C) 2015 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_CORRELATION_TrACKER_ABSTRACT_H_ -#ifdef DLIB_CORRELATION_TrACKER_ABSTRACT_H_ - -#include "../geometry/drectangle_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class correlation_tracker - { - /*! - WHAT THIS OBJECT REPRESENTS - This is a tool for tracking moving objects in a video stream. You give it - the bounding box of an object in the first frame and it attempts to track the - object in the box from frame to frame. - - This tool is an implementation of the method described in the following paper: - Danelljan, Martin, et al. "Accurate scale estimation for robust visual - tracking." Proceedings of the British Machine Vision Conference BMVC. 2014. - !*/ - - public: - - explicit correlation_tracker (unsigned long filter_size = 6, - unsigned long num_scale_levels = 5, - unsigned long scale_window_size = 23, - double regularizer_space = 0.001, - double nu_space = 0.025, - double regularizer_scale = 0.001, - double nu_scale = 0.025, - double scale_pyramid_alpha = 1.020 - ); - /*! - requires - - p.is_empty() == false - ensures - - Initializes correlation_tracker. Higher value of filter_size and - num_scale_levels increases tracking precision but requires more CPU - for processing. Recommended values for filter_size = 5-7, - default = 6, for num_scale_levels = 4-6, default = 5 - - #get_position().is_empty() == true - !*/ - - template < - typename image_type - > - void start_track ( - const image_type& img, - const drectangle& p - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - p.is_empty() == false - ensures - - This object will start tracking the thing inside the bounding box in the - given image. That is, if you call update() with subsequent video frames - then it will try to keep track of the position of the object inside p. - - #get_position() == p - !*/ - - drectangle get_position ( - ) const; - /*! - ensures - - returns the predicted position of the object under track. - !*/ - - template < - typename image_type - > - double update_noscale ( - const image_type& img, - const drectangle& guess - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - get_position().is_empty() == false - (i.e. you must have started tracking by calling start_track()) - ensures - - When searching for the object in img, we search in the area around the - provided guess. This function only tracks object position without trying - to track the scale - - #get_position() == the new predicted location of the object in img. This - location will be a copy of guess that has been translated and NOT scaled - appropriately based on the content of img so that it, hopefully, bounds - the object in img. - - Returns the peak to side-lobe ratio. This is a number that measures how - confident the tracker is that the object is inside #get_position(). - Larger values indicate higher confidence. - !*/ - - template < - typename image_type - > - double update ( - const image_type& img, - const drectangle& guess - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - get_position().is_empty() == false - (i.e. you must have started tracking by calling start_track()) - ensures - - When searching for the object in img, we search in the area around the - provided guess. - - #get_position() == the new predicted location of the object in img. This - location will be a copy of guess that has been translated and scaled - appropriately based on the content of img so that it, hopefully, bounds - the object in img. - - Returns the peak to side-lobe ratio. This is a number that measures how - confident the tracker is that the object is inside #get_position(). - Larger values indicate higher confidence. - !*/ - - template < - typename image_type - > - double update_noscale ( - const image_type& img - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - get_position().is_empty() == false - (i.e. you must have started tracking by calling start_track()) - ensures - - performs: return update_noscale(img, get_position()) - !*/ - template < - typename image_type - > - double update ( - const image_type& img - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - get_position().is_empty() == false - (i.e. you must have started tracking by calling start_track()) - ensures - - performs: return update(img, get_position()) - !*/ - - }; -} - -#endif // DLIB_CORRELATION_TrACKER_ABSTRACT_H_ - - - diff --git a/ml/dlib/dlib/image_processing/detection_template_tools.h b/ml/dlib/dlib/image_processing/detection_template_tools.h deleted file mode 100644 index b22c109fe..000000000 --- a/ml/dlib/dlib/image_processing/detection_template_tools.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_DETECTION_TEMPlATE_TOOLS_Hh_ -#define DLIB_DETECTION_TEMPlATE_TOOLS_Hh_ - -#include "detection_template_tools_abstract.h" -#include "../geometry.h" -#include "../matrix.h" -#include <utility> -#include <vector> -#include <cmath> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - inline rectangle compute_box_dimensions ( - const double width_to_height_ratio, - const double area - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(width_to_height_ratio > 0 && area > 0, - "\t rectangle compute_box_dimensions()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t width_to_height_ratio: " << width_to_height_ratio - << "\n\t area: " << area - ); - - /* - width*height == area - width/height == width_to_height_ratio - */ - using namespace std; - - const int height = (int)std::floor(std::sqrt(area/width_to_height_ratio) + 0.5); - const int width = (int)std::floor(area/height + 0.5); - - return centered_rect(0,0,width,height); - } - -// ---------------------------------------------------------------------------------------- - - inline std::vector<rectangle> create_single_box_detection_template ( - const rectangle& object_box - ) - { - std::vector<rectangle> temp; - temp.push_back(object_box); - return temp; - } - -// ---------------------------------------------------------------------------------------- - - inline std::vector<rectangle> create_overlapped_2x2_detection_template ( - const rectangle& object_box - ) - { - std::vector<rectangle> result; - - const point c = center(object_box); - - result.push_back(rectangle() + c + object_box.tl_corner() + object_box.tr_corner()); - result.push_back(rectangle() + c + object_box.bl_corner() + object_box.br_corner()); - result.push_back(rectangle() + c + object_box.tl_corner() + object_box.bl_corner()); - result.push_back(rectangle() + c + object_box.tr_corner() + object_box.br_corner()); - - return result; - } - -// ---------------------------------------------------------------------------------------- - - inline std::vector<rectangle> create_grid_detection_template ( - const rectangle& object_box, - unsigned int cells_x, - unsigned int cells_y - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(cells_x > 0 && cells_y > 0, - "\t std::vector<rectangle> create_grid_detection_template()" - << "\n\t The number of cells along a dimension can't be zero. " - << "\n\t cells_x: " << cells_x - << "\n\t cells_y: " << cells_y - ); - - std::vector<rectangle> result; - - const matrix<double,1> x = linspace(object_box.left(), object_box.right(), cells_x+1); - const matrix<double,1> y = linspace(object_box.top(), object_box.bottom(), cells_y+1); - - for (long j = 0; j+1 < y.size(); ++j) - { - for (long i = 0; i+1 < x.size(); ++i) - { - const dlib::vector<double,2> tl(x(i),y(j)); - const dlib::vector<double,2> br(x(i+1),y(j+1)); - result.push_back(rectangle(tl,br)); - } - } - - return result; - } - -// ---------------------------------------------------------------------------------------- - -} - - -#endif // DLIB_DETECTION_TEMPlATE_TOOLS_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/detection_template_tools_abstract.h b/ml/dlib/dlib/image_processing/detection_template_tools_abstract.h deleted file mode 100644 index 30b0ad5b9..000000000 --- a/ml/dlib/dlib/image_processing/detection_template_tools_abstract.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_DETECTION_TEMPlATE_TOOLS_ABSTRACT_Hh_ -#ifdef DLIB_DETECTION_TEMPlATE_TOOLS_ABSTRACT_Hh_ - -#include "../geometry.h" -#include <utility> -#include <vector> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - rectangle compute_box_dimensions ( - const double width_to_height_ratio, - const double area - ); - /*! - requires - - area > 0 - - width_to_height_ratio > 0 - ensures - - returns a rectangle with the given area and width_to_height_ratio. - - In particular, returns a rectangle R such that: - - R.area() == area (to within integer precision) - - R.width()/R.height() == width_to_height_ratio (to within integer precision) - - center(R) == point(0,0) - !*/ - -// ---------------------------------------------------------------------------------------- - - std::vector<rectangle> create_single_box_detection_template ( - const rectangle& object_box - ); - /*! - ensures - - returns a vector that contains only object_box. - - In particular, returns a vector V such that: - - V.size() == 1 - - V[0] == object_box - !*/ - -// ---------------------------------------------------------------------------------------- - - std::vector<rectangle> create_overlapped_2x2_detection_template ( - const rectangle& object_box - ); - /*! - ensures - - Divides object_box up into four overlapping regions, the - top half, bottom half, left half, and right half. These - four rectangles are returned inside a std::vector. - - In particular, returns a vector V such that: - - V.size() == 4 - - V[0] == top half of object_box - - V[1] == bottom half of object_box - - V[2] == left half of object_box - - V[3] == right half of object_box - - for all valid i: object_box.contains(V[i]) == true - !*/ - -// ---------------------------------------------------------------------------------------- - - std::vector<rectangle> create_grid_detection_template ( - const rectangle& object_box, - unsigned int cells_x, - unsigned int cells_y - ); - /*! - requires - - cells_x > 0 - - cells_y > 0 - ensures - - Divides object_box up into a grid and returns a vector - containing all the rectangles corresponding to elements - of the grid. Moreover, the grid will be cells_x elements - wide and cells_y elements tall. - - In particular, returns a vector V such that: - - V.size() == cells_x*cells_y - - for all valid i: - - object_box.contains(V[i]) == true - - V[i] == The rectangle corresponding to the ith grid - element. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - - -#endif // DLIB_DETECTION_TEMPlATE_TOOLS_ABSTRACT_Hh_ - - - diff --git a/ml/dlib/dlib/image_processing/frontal_face_detector.h b/ml/dlib/dlib/image_processing/frontal_face_detector.h deleted file mode 100644 index 3f4b59769..000000000 --- a/ml/dlib/dlib/image_processing/frontal_face_detector.h +++ /dev/null @@ -1,2373 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_FRONTAL_FACE_DETECTOr_Hh_ -#define DLIB_FRONTAL_FACE_DETECTOr_Hh_ - -#include "frontal_face_detector_abstract.h" -#include "../image_processing/object_detector.h" -#include "../image_processing/scan_fhog_pyramid.h" -#include <sstream> -#include "../compress_stream.h" -#include "../base64.h" - -namespace dlib -{ - typedef object_detector<scan_fhog_pyramid<pyramid_down<6> > > frontal_face_detector; - inline const std::string get_serialized_frontal_faces(); - - inline frontal_face_detector get_frontal_face_detector() - { - std::istringstream sin(get_serialized_frontal_faces()); - frontal_face_detector detector; - deserialize(detector, sin); - return detector; - } - -// ---------------------------------------------------------------------------------------- - - /* - It is built out of 5 HOG filters. A front looking, left looking, right looking, - front looking but rotated left, and finally a front looking but rotated right one. - - Moreover, here is the training log and parameters used to generate the filters: - The front detector: - trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml - upsampled each image by 2:1 - used pyramid_down<6> - loss per missed target: 1 - epsilon: 0.05 - padding: 0 - detection window size: 80 80 - C: 700 - nuclear norm regularizer: 9 - cell_size: 8 - num filters: 78 - num images: 4748 - Train detector (precision,recall,AP): 0.999793 0.895517 0.895368 - singular value threshold: 0.15 - - The left detector: - trained on labeled_faces_in_the_wild/left_faces.xml - upsampled each image by 2:1 - used pyramid_down<6> - loss per missed target: 2 - epsilon: 0.05 - padding: 0 - detection window size: 80 80 - C: 250 - nuclear norm regularizer: 8 - cell_size: 8 - num filters: 63 - num images: 493 - Train detector (precision,recall,AP): 0.991803 0.86019 0.859486 - singular value threshold: 0.15 - - The right detector: - trained left-right flip of labeled_faces_in_the_wild/left_faces.xml - upsampled each image by 2:1 - used pyramid_down<6> - loss per missed target: 2 - epsilon: 0.05 - padding: 0 - detection window size: 80 80 - C: 250 - nuclear norm regularizer: 8 - cell_size: 8 - num filters: 66 - num images: 493 - Train detector (precision,recall,AP): 0.991781 0.85782 0.857341 - singular value threshold: 0.19 - - The front-rotate-left detector: - trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml - upsampled each image by 2:1 - used pyramid_down<6> - rotated left 27 degrees - loss per missed target: 1 - epsilon: 0.05 - padding: 0 - detection window size: 80 80 - C: 700 - nuclear norm regularizer: 9 - cell_size: 8 - num images: 4748 - singular value threshold: 0.12 - - The front-rotate-right detector: - trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml - upsampled each image by 2:1 - used pyramid_down<6> - rotated right 27 degrees - loss per missed target: 1 - epsilon: 0.05 - padding: 0 - detection window size: 80 80 - C: 700 - nuclear norm regularizer: 9 - cell_size: 8 - num filters: 89 - num images: 4748 - Train detector (precision,recall,AP): 1 0.897369 0.897369 - singular value threshold: 0.15 - */ - inline const std::string get_serialized_frontal_faces() - { - dlib::base64 base64_coder; - dlib::compress_stream::kernel_1ea compressor; - std::ostringstream sout; - std::istringstream sin; - - // The base64 encoded data from the file 'object_detector.dat' we want to decode and return. - sout << "AW2B5ZIvv09mlKLVYjKqbJC05yeR2KsCpPGEGOgn2QlwM92S4UT4HgQkV0V9WqYRf6xETTSVKz7Z"; - sout << "YcJ84Jc4C3+VdPgZDhV+LDt6qAt3OI4nA9zN4Y9cCIb6ivlETkN/JMmapbOAUW2mrSzDif5zjAaq"; - sout << "+NFvw/5V0Jciopw9tR6nYtV41unWGvyyfsO9CcqvDy81QIydToHh0a7UaL0jCtA2DYzkViDufxyv"; - sout << "Kpsn4xMyiU0haM1ge3UktIO48io/gSzjEKu0YYAffbD2YO1IE34tUH15Z3Z9NjkBFxTytDgrMxk8"; - sout << "i9MYq+Nl9nS421aogmec3ugExJYjLZMHs4KAk71jvG8vtJyJEA3qyLY6lvONt98gzQwGQ9+2B6de"; - sout << "ocb/DDJUza6mvudHQNJBYraR4gCWcIn9gFu2rJiRHf4IiqP4GEB3B1zKiHfJRo9jZbhxQUitAxAx"; - sout << "U/E2SuuHGZDilqK9AJ4K41RAudraxF9li/Bs4f+CK3G8Z/c97P7WLVekJL2ws+MsCdL9ObHE5ePD"; - sout << "uLLQWBy5NUbgPVM6HEnhnOiZk3rA4DYNqbABy3uemablAln9BLGkk4wrm2UcicacnzY8Aq054Ttb"; - sout << "3CCTcG4SOSPfePl/7T1M6Uy1hOesp5MpXfUR8gBKr4466dbdXCDHSahI05gra6NzxkOpOo2mOqBg"; - sout << "LYNGZUkHK4tdRyyD12N1MH+nJiMJbgk+qj54t5i3AuEr/71HTRXoTT8AEYbvc9y4f2WAlliQYXPn"; - sout << "O2Uaza3lKYrH7mFjKMNhLfvrezy9fe+1asbSlRKelnU3eY4lhD6fTVJjXqZypBfMnfmGQQJ0Q7g5"; - sout << "1Z/9GzpRyZnPSzQljtJgzVp8Gk0z3fuKiXPO9g+s4XL2cEuxBOFij0KGTy4eNitM0gcPc6xzp3tz"; - sout << "6Wv0W2h7w4h+V8Bzvyn8ag1sbEO0G1Lf2BrDVM9+pNxFoWFxYHqdoOmJPVvb8PRQqoC5bkqhplFr"; - sout << "TR5l3XsQedgwsnkadxNZQ3MbRJyo0JU0kvV1cfphLcn24MIIKqAnw3daXqbJaba+oCUep5GTuzI7"; - sout << "nad7ykHNN0iFkgYXMmXJl+F5TsS8y+izuHlXAX6wX1qRVzWJwCpM5oVVG/5eYTzg0J9C1bCcNyHL"; - sout << "2w5TJFYrD8bq3O+Y3fiO5LJ8F5/vsu2EBUMi1+eP1WfsTwd6N9jFtF5gA5sHX3zI925aDqVx9byr"; - sout << "j4X5yr68p5P6f8wSLL8jzW8i4a0yP3zXlqN6QQDY1ssfNsMf43tOTtmbBlmxviL2egs4gvadD7Gd"; - sout << "fRNowL71P3mkqRmnrnihlI01NbDl+Trzsh3EOn43PRC9nl8yo+fYVH8GqS8JGy1xOw4G479vOifI"; - sout << "9GC4BGnSDJdKgSnBwI1AJQ2TT8EZ//56lkRlgusg25TwC7uQ1zreeL6baYdgfXSggx3ULdNDGl5o"; - sout << "ftRK9LDaop6XvB6I0ITsLYvAoGP/5sHfttDj6HlQW/LlzkSPmzY/FtV6h6bE+k1gG7BANrQjwOW5"; - sout << "sfHNYadD1v4zIFdt2su3docGbGP/iDMvM+BmYIBP86zX5eIlTYwDmxXht95T6GCCjS/XuMMy12hd"; - sout << "Fdb6lm1O42ieM4KQ/2EOFy3Ij+YOIapzYA6p6Jz9dtINpCojgUHyo6xc4HTNnEKRy+YN+awhb1l2"; - sout << "FJdy2/QI3xGVNNTnWcQrsvjGZb/Z3VaZUltrIbnCeEZOeOCM0TxkBEhqFfI3qwMx8PUj+imUlTDM"; - sout << "7N+p5sxmKLliHHovOO32ajBTKUSI9IMQzf3QY6dZDts4JkMYQ1xc6lpm679s1KMVVrWuOqiAU5Vs"; - sout << "qehfnl+oMRngi0G0BnMne45CjU5RECvhg+Vkkxx0kAp38+9pY3XiO/DuyIxpOSPip2o0+9rZLF1Z"; - sout << "cAUGnG85CFEXl96wpxvqVlIULUV2+pNJxdU+q1MkCsxDeXrvfjhEAJpPE38dUb3t4blsNUZ3wJ2w"; - sout << "s6cXe0nEPWNkZlmEsXcFpw5zHe0Gd7YpXigz7Z+IVhvplpv686TJiLTpVPW2T1uJvSmMuG/FqvT5"; - sout << "JIIMg2of1ydicw5EbWrqhIUzllX3l0u00gFziPmKAioiqCxjWojd9l3Q0Q6IsaZAH+WzV2xFabbY"; - sout << "4b8SwoFvhe4qnUQLFdOSTbzeDIKP9B8bSiQwbjUBg3jYEWUrMz+eR9lpGu8603vChIEXaTxyMrO5"; - sout << "SCeaVOgPE77potDoSUV1hsoW7ZqGCFH+AGyVTohitS0iqZbIxC7+7rnVP8XfXw5YpSajF94z2TSd"; - sout << "jW0KpmuCZ88DTCPFamf5zh917qp/PzQOGTdalr+Ov+ogvrJraDnoE+ONWrdHqBm7Adgn8/wy5vzX"; - sout << "fNu1AT14eYrEmWmXvt6JDAbBYqP8Aw8b1QRZff11MblUh0IpztedWhifGy/RFJUN0/e66Mh0cKeF"; - sout << "plmK6NqchTzOQMKJVq9jxdyurcjcA0uu4dVJ1XXkAtxBim2J2m0zcwX/+HcRe9VbeNehmDbUC49o"; - sout << "ktNvrwbbB1IUV/c0MNCruV359DVINXskQTK12g2X5qprOLW+YPO6CnTFpJRsiFBoLllF1sUTjROH"; - sout << "SrHHRYp3W5t5gqfT4afBxmtTmpJEG0oG4eNfMhxEhQ7HjoVhahOM6px9Be9S+4ca/w+zII7NnUkY"; - sout << "Iaas+FW7vhOIDOiV82SpJqBjdY9eIP//XGR1DFQKI5cLKmT2/DF8tB9XcqTgmVWNMVt9Xw21CaeR"; - sout << "eYeoWvLHlm8o7ahtJCSQ0iHypTZMA16wdJ5IJD5WoYd50rUn58RBa9sTXT/t/KhxJfG5OWXl55eq"; - sout << "abYojSlluFyvFSk7Z/wu/EqFUEBD8r4OIrlJCMZl6kKy4EncmjUrb3mG6aDKxsaRBRBkRRya9t77"; - sout << "epMG60v3MRCcY9E+n9sXAOUpf+ErN7iD6FY4XFpq5R0Z+6MiLRE0af/JQ9R42quTl8CLH7609DDd"; - sout << "s8+8bKA2zjvSJhWbwGURRCW8SK9tNKuemwkt3Eutm+xMJemP2JIVFVXYxjCvDmxIIODneu1vmcSy"; - sout << "XadKkyjtYDwacddFAqGh0kLqHX9i/WoedVKC1Vuup+AYPkyZ1lPraGVqjq0nsiwp/vxm9c/+4/wS"; - sout << "hW99Q+zoAZ0IWWeYAqcXGdZqvd58gx0/fmU/Pq4FqtCdJ2qnoUDMvjZeyWE7lA/Xf7AdLcz4XHNz"; - sout << "VAidxMj7/K8p3KdK+XqED94Ey1WzpUQ2mH+10Zq/6jebtoYJlht9meMsjvjWxg4nwFIZY1QAMZPV"; - sout << "phcmEwrLA+Z/Xjo+FEq4hKD8pIriQi4xT4uAoPzOFGp/ziwBbAb/EfYsspnVxpnERKblbDsV9bFK"; - sout << "df5VSgeqg7p2auZBk/WkX/wOeXkulbiJA5lXTsgInJGoREJ+uaudFadnLD1pmjqq+VFJW6XOT++I"; - sout << "arRHT1sYJY5mhqFztTeUGH5VXZNtRGl1nWpewvmgyK6T5XLUcuZqsyZVtzkkQ0eSR4h+nBuRAQZK"; - sout << "EmqzcPrRKObVC7Xv+kMcnM+M2+zCuZoUSO/zt7OOXNt/B51oQ2DRqthPxgzUrWOvoOgZleeayImR"; - sout << "rqG2QnkA8+Kb/mhxJ+SAqOjsIJATfLc13SzVIKVumz+uX5jUiZXWfWF/e1cdS1w3Nf+dNinnGQ2v"; - sout << "Vf2SxiRlTfDTZTZXcLlT8VrCOP4UYvg0QrzBqU2myM41lZDUS2X/WOzvrNrRpEoCS6/OcbvMj5gf"; - sout << "dzoZ1oqvaL3dosQ9/QAwI7wPC2/QQTRyDIbl4EuhcX/ebyueLqlxKRLPrmq/mE2YU6aU7cf+t8PM"; - sout << "LX7J0eyNGl00TEWN0R7ui2xlfdnLfmILs+lNNthYvmUbtbncoqx0sCWgjk1Iqagp9uFlWA+6vMa6"; - sout << "nx7Qg0Jz+Qn2u4iZpyGDZKUWmHgDYhRcKfsnjnzbztyNms4tEnmwtIeLwDqFPlC4BKefz9gja+tt"; - sout << "0Om7TAtwcmWHve1ENSOQSKTLNvVwhjRLgmfK9SFUUjKeVSMp1g27Rf2WCoDyBXhRauHpqCdj7GH4"; - sout << "AmlI3EUHSjS+/1ZoT/2DnHwuN+GKVh0d8k/7sGrln94r3JuxEPvfyQFvPSRlFkWYyKPdxb+H37L7"; - sout << "DCYzgf43vxZ82JGYVXB7QBpO0VeEXiQcIgXV7uOsGKiXfFUwueL4kdNknk2hAfrFFQcpoBiQSz89"; - sout << "sNRT1tH5Ipbuf19R66cogiaAesXnm42jLjQpnkMwd3S4F6P8zDL7m13u6ahcrWvZiyDuBc/+Th2+"; - sout << "Swex+BSv2TkUorR6nwVoozQEA16/MlZB1acIFSrD0kSr22Vubdbo05svEAZ7DKIdQjDu2wTTOOvL"; - sout << "IVJOYPjdxXZytBv1jRIhUyyYBvRtaFsl57ZWAmvbFEXZXLihnrBskTqxrxNPqhg+bLxicTDlFyHI"; - sout << "UIipzL4AvofdYWolB8RvFyom18/szC67Flr1OW4axZ+k5S7249Y64eqtU1hk98joIhOdWaWBHxkL"; - sout << "nP+ooeHeEvB5hNRIA23Yxoh4zzsWUB1KKvg1XRzjt2CBQ2FPaCfHsOKf52aaj0W2FByC81rpryrm"; - sout << "Ye51T5zP5/N5j7yA+a907774PwIS3eYYyJRUCSh0ywfQ8rgkbjBdf2rKa0alzokz7Kmo2Iswnid0"; - sout << "WzpliQr9KaPwAk7hkLjprMjzdJIug3KOKVAgygXP7rkgETIfTfZRG49EdJOjlW8mlmHZsO+arTFW"; - sout << "vj0FgJCAQrrX0X9BOQ0MPu0friAGK0TNGsFs17lcHjaRNHXz3v6dY/MSR2TY82iyEkRofvNY/Xjr"; - sout << "FRB2KM6Aq2pPpIjY4EuSQS5sU9ur5oxrKo68jNzoB9iRvmKhQq5HRSYKL5ACBF85HM2oWtyVl23y"; - sout << "TqTW+jwNHfF+sc0FPS6xfwr3yvHi/OVlW046gnNLKOxO3RjGntaJeX8EVXGGpXDHLyle0UaZE0iG"; - sout << "1xeLZjyKq5wRJ/Q1MPry/JJbfCXSIHeO2Uznqn7O5rcs5v3Z1PdlF7BfPUhnP7+Wcqryfi4xJ8rS"; - sout << "BzyJkibOCzegvXnKTTw7q5/lrgh7LxfrY/4G6/Js8ibrUU9NGqBkOHUmxa9P7UPK43pz/bS7SWtl"; - sout << "yA/3hBa0bv6hN0OeXVaBxtr8sMfS7FcvR3wtvmtKn4BlIYer3LMSvigPCK3K5seTPH3cx0J2uGzf"; - sout << "SlPZus5idN8MnFCEiBUbs4W1M/BSw9EYA9rJyhDTyOYqKr6s1kagBUoVCXVlEPVgrJoppc4vLghu"; - sout << "NMgUpcakhT8SAulssCjPb1UWPF92XPpn8/byK8dJoSFe1lfFb5Yog5YZMjgoKKbokk0n3eMlrbm2"; - sout << "AGwIh0acdOXRR+lpeJQ240N/Waw3e+FhAI+AYfOkIXodtQcod08+F8uHCAAcd9dZvYyxZxNKjbCc"; - sout << "aYTFUYPN53OZEwEyCIFWwPf0QhdhlpyAGCj9gqVU4N9b5FJYX2ZqVAl5JF4nl9yDWrJ3zmhwL4r1"; - sout << "P8Pdv02ysNeZu76Y60+ffPXCqmjHjllu082gde9BXIEWdS1sd5qaH0qb8KRpV8WAYaM7/ccGTHQ+"; - sout << "H+0C5o2904WS3MG8rR6LI6EqO2fcBnJzZ5BJX2bHv4kNHhQiW2tZjBlwKjuMH8Ddayd1BVqzjeuH"; - sout << "5dfcL8xV4su36eRT/Vmanq/NZ80+KXsXZO1k88RIfQwwZdt5XribJfUSwzKGsKQrhu+8iUCjGP8l"; - sout << "ScrIRdj3gjy2brM+zBr9z9pvFZR5NLjYN1Ko2BptMbEDxdjnYkYWix8BF1P+/PtSEJeGATIyl2al"; - sout << "rAlEHX3ysdDjUic86ZNUx9c6N59ZcQkIr7IwFl6kc5sbuthroXmAnbW0A2UIO/LN6KFbbE53Up4Q"; - sout << "KwoMeMHxlgEwundK+LV5WZ136K5JoA6SpvxzuKhCckg0Ev4+KtyA+1wlna6AHOQaj24BzblSd9k4"; - sout << "2lWsVOwAOtGxFIRIxpou7S4yqPrvS93KPtVkrHDBqIveGcwoGfyw2ZSX+5o5SIZ5PUG3mFM/sNWw"; - sout << "twketaHdV/ndITa3aJyGpqChs3hcwMOgODnpC+vjtY1D8zdp3pn4MBgb33jxc5kOCpDktiKGyaQN"; - sout << "sQ7oaOy4aKmr4TFfWbrH3qeR9gz0utGL/iVHcgSlfl8rw4BFncc8HIB0SGJJhYE+lfEYpsP8H+1p"; - sout << "pfG0yzIA752vcaOIWIGt+C/EvuXl5PP8qyE0aBe637yQd1aMyRhf46rsAIlhzwZ28wPYZ9KCaC41"; - sout << "ap2+7/EJMw3HramAAo5OVqA6M5cV2V+MlGifSoVgTN+5TaY9EnqexQy2Gqw+9484Tv7QNVaEtwtY"; - sout << "/O+aQ8nzc6H+clWCWJkWDvoqIrIqP4jFUaJ/FnqlPEb2GkPoNluJV92HqQj6fD2Iz0TQKCVQkVWq"; - sout << "D/QuFVq8c+EC8Bz2j1cI0D30iwROmneb6XHTYVwn4yHkZ6LAoOz28fjT6dwJFdYo+Ci4Hhyl62tW"; - sout << "P0al3X19i/IjH4Xi+ZH+lISFmA0oEJo4AG/oAklXtGtRtIwfKGIuIzqqEztmX9tY+INu7PtgH/FP"; - sout << "z7d2f3CBZTZY4qTPMEPQ/th8jnjHrROZIM7Cej4v+zYms7NPlJ7x/k+eX5ISG7xEbWr8j+kr+R70"; - sout << "bjGaz/rED73YxTMBmhQSKMDUjNaW/qclrQuvaNwUgM/VCtnY7NANztFMXhCa2hGjZaG/bp8Yc9IN"; - sout << "T20nhrbTX+KPkcEmQjsHwyK8hT9XN6J+TD4iwdnb4A/KQI9JwaqpYPp0S1d99j0iqXlirvdPcotu"; - sout << "AsUmNf1YOlK1I5KxaFA42emXXmg1vr7USuKiX62IslSjknRY0+bPxOcn09P0VK+HTTdLIZ+8p9+k"; - sout << "fKsgY8ajl6qZ/LP5qbZ2KgHJHJNArwRSxNn4CR5ish97R+1A3DglEaWJ4vVuu4oaFIHc9eSgRMdJ"; - sout << "IPJ52p+8SKIpjM3Tnig/Gw35R+sPcuZlpauFplYb3vcIoY7vN/f6+RKxPtWnuOfBh1iPJxJJfz+H"; - sout << "MDVZihR471I+DLXgGrZ0fgMQZqVFelhF5eszKMOxB81TbRuPqUmneRijtWvR8QAySqzV3o+OoM/n"; - sout << "fpoLxmcVQm3LanGF1VfCbk7X9dhocgWTpk9XjDzVjIjPceJ2IPuFjHcrNtu6L/fe/6sMqkKWNRQH"; - sout << "8GGbrJU0/kqXIeYch5gXjiKFTIU/QIRt0e4YKlNV0Rpqkh8vY/X3OL8xbNCBd0bM4nCXMp5Ytwyw"; - sout << "DEyjzBl4SvxgGSqG6ehAzY1LrZ3bHU0Bn/Q7vD6RIEr/WcxUvdr8oy1JuIey4PllgfcCaDdW1+wG"; - sout << "YCz/81Acw7xOiG//gLZ+tApj+tGpMP3Z/vnC7bZmXAmXWCfZeWDwIcxX/V5Sco8G21PpzMYPM7k0"; - sout << "1MEkR1PgNhpKv5he6criGZ6D/xVAfVJbxc7blkovBkLh109MFBCAGiA8zk3MAShzI7cynZVbyWGw"; - sout << "+x5Bvl8/6xSUyG0MLFANqDilWFIEBpT8h6G6StX3WXoEfqJrO2pYMjQdqOg08AvXKWJg/xj4U2Mw"; - sout << "m8+nK+zX8aXHC333WcQ+1eG918/0TEDoQAXep1atGq3wir0iBvurJHbOXffjGQalMd3AeFCLWaFn"; - sout << "7tYSTWYcPnWwWuA47FxTSOPezm1PrihBIC7CyVjGHAGvtBdh2EjCVptJHYgft9Ivp0YpPFaGtT1c"; - sout << "IsaiWl+dF+Yg0K4FVIpNqRq/g1EEpGni0mrTmlTKeeSiKzAXdGnjOZ/9woea73BFZkY0kAqMlrn4"; - sout << "6AOIuXh9Af1UJe0OxzhcYKFjFuzj7Imjv0SgNaah5XePYFfLyqNUCctmTlFna9nZWZ4/Q/N0tqN1"; - sout << "QJwMtZOdFdKoSwFcDrSuMBc2kKNCEgnXAB9azTyR6Frs6RDNbCOdmMKEIF0Ra6v4fqO/rzc+m8nM"; - sout << "2GAyE9yBNQq1THcQSqlataFHDe9KkmlQ41F9hKifZEPJ2eMe4WbpMdXmjT0nNmxif9OiPMKR28EQ"; - sout << "pcqtuJxTE2oQArxmoOD6uUSUpm+Xc190raj1/JA7kfFQPkONEkNn9fYRh7J9VvPk58RIkyDL3RfG"; - sout << "SjlzXsvz0d2uU14U8ppyPSOUEgcvxUu9Zk/TcwZkWvQeJTPd/i7jbUyAHTPXy0secfKXWSoF4T1S"; - sout << "AuuRuErtEIXmJm6bd3v6ozR+Vc494q5Nu80EGIEy+09XWaDi8E0ChYGPUn15jWmkw9aZ2SUGju+0"; - sout << "OS7eaGTSBcbS7l3IaP+053oZvh6NN+iYo5Lb0rs+bog58fqpXLFLeaJFnHUmZipr4oX2EfpI3FuE"; - sout << "1I7xjgdZiWMq57u9UId2PuNahTVN62Du790tZhGfoAACZxKx9xxi8nRwxz1Rh8uFosXdHJridfzF"; - sout << "gzZhDTmxJjYCq7tg6769BcDtHxT2G+JOh2hMFV+aieGZEkBEfj6EWhuot2jR+VVjpLUhys154Fj1"; - sout << "NLN0d0zMnJDThQlNGigIaHgVdQ+l/lNtN9ovAuVJRib/fYnSDRBpOQpOU5NuwyHjeHnYg20iKuBT"; - sout << "ZWphFPD7M+zYlrVVH9Lg0AsaY5Yt0U7g+TXLuT/bi2tUz3rrrk/5bY7iLkGbEmOFZmxzXXqWdm8h"; - sout << "ENOKVj/yrgSa/l93WqyOESSZk7hLMvP+OVkSj8qAKKBQ1+XyqVLODZZae0volbIcZe3HAAIjdYTm"; - sout << "+JQfIGAWgkqcHwgv3WJiGPhq1WOVi4FSq2Dgxi5/J6cRg1Smsr9aCx0uNC2x362lI8Jd9yKn8m+m"; - sout << "te+3Zx6sx0NCnYKxaWcH3V7BfF0hp0WQJ3vQbPG20PD/ACHvMEgmo2dDFit6m4yfWAQxHzQZE/3N"; - sout << "E5TLT4EMnZxi00F6sV0G25nElE2t9CrGkLNxTUbK2sGKx+ybsveIWpoNtQty7hY8NF2KIICOd8QJ"; - sout << "FsAKxGHbydI+9NV/8KyW2UID4JpoNJOQkh4B8pp/1bkBRPsikKLyowC6RWuWmVBm/DCPSIwkiV2A"; - sout << "jNHVRmSoDO+U3eTMxbamjBV/H+xWgrBzBu+4aaFGH0MbKNtXG5COeCVMCtA5v9pmR65GLD/DYWcM"; - sout << "JltMV+H82nUN8qDVTMpCSzrlkiv4Gmvh6b9HkxZC03g+IrBKAkXWkhIl3iYkIjLYNudFSUddDb6g"; - sout << "/wHCk0lGJlbYim9VV0uRYJITZenRrzsMcb6g6Cm22cB9awV0qpixCGVW+jms3MfgcstzqdN36KPw"; - sout << "C0IDdKjN2Bu9aNqHqWafK8Vl+oTYVU6foPJSOmHD3MhFHhuZk0oPtptRs/0aSZKH3FI+jz6KyTM0"; - sout << "E9UDooIsxYAo7og8Ka1QCVel8cH4mmTBWTGLNNxuVwvHYgQc+j+QgKJ8DX3XzEHJQVL2fxCmm3i/"; - sout << "tjJTltGK8o3S66OO3dN5g1KaYyxCDkmKjsGpyqAKGhdrzQzwLru4oof/b4cM6E/3aqGWH9pI27G2"; - sout << "8jNYhu6r5LhYMpczurY9gssS94+RdUn4UFMt2zZSlpFsCY9E0NNaGwQ5sX0pcyk9r/FKWAWxT+e4"; - sout << "b/buzfSIVsHWrzytkKOYCHMylaPd+juDOWX/Y1x5IBmR/VnpsIWbuYFjRlK7bvNoVcwitIZyI1Ku"; - sout << "vmkH9u5YzndkbH9fj8FroFgMdZumIeRSFz2448yoIh/1+2wyEUXUvof32q5kktEutky9XtKCTIen"; - sout << "LlWO9/7k0Kcz2Cp1S8bugmULKSLHEWMTtScZhEOl/o3jyMjLpbHhSfY5IHwZXVp6MO/bxpk4F4ur"; - sout << "C2eAlsHUW0484VZFIm/GtgNRKq5H4MTRSmlzHxh0o5KnK87ZZNGKv2sGFoxhOKT8g9s8uz2ZfkI8"; - sout << "HS0VWQ5y0Y9dY00ShJj9FShAuForC1EW8TBcgW2wjk7uN4CjXgupadGHC4hMFxVjJJ4tPj53PX+w"; - sout << "KKTety47QKF0aeNAXeiNkzo0e/H8XYKYvyRKPpUhWbj5rzdkSev920dKjpq731kGRLUP8kljqmx4"; - sout << "j/1ukvHqJrarb/U0LWECXe1mHUjehedJNCuDsXmlX3OIT4557z3W9vMbzKyu+0R+LN2YtYUFTdXA"; - sout << "z442W3oqy7cJRIMfioDLTO4ry26sNo4uyq83j3iFx2iY4Wc41ZUGg9cwh5TVKg8XEh5US5xlsqVO"; - sout << "kDR3XfYXA3GwuKklaNN6vImd+oP4g2ZYSl51f7tj5hd9xpTSRwIy3RJJ5VoTz+36jpT4Y3fnlppg"; - sout << "GqBmWhJrY5UemTIoZbJ5X12NjQjW2HiKsuiCpLS9Wm0IXYWcRSYfiWYBLP+QZFyRA2VqVpwmY2X1"; - sout << "EafYVxAjG2au8TnbfK+PLccuRg+kYNExJfD/hLUMyVg4wkLxP95L8CB85+g/1VomueeKJFnlrnkO"; - sout << "ezCBls31aI/r4fMbdISFALkRwPav4rVwi8M67zuhxx/K97+5I4ONkaSU8/DI4SpqjfEIzl/y07Rg"; - sout << "VUou00laGIhidjtfwENl18fyXGmjmLI/Mn+/H8gU1mW4Z0stSN/NkPYZjTx1AnvjG/LgaY1750yS"; - sout << "4dk+ygLr07oWPhGB3BhIElS7VDxZnnPo2MFIPXTqWHqZ1/lNq8DE2EqgHgpFQGmp2MZVi060DA0Y"; - sout << "En5g8zk1NXq0irzIv/hXYLbDEnL4ieulF+BlWN1oeERYelY8VkqgMtqGwBlwiO/qN488MVobHHAk"; - sout << "VARDBpkSyX2bsF0KS4BCwybuQtNPVCaozYKWd8Q0RSNvsK72afBC+snd/y2KrFhcE4mE9ZhAwV7R"; - sout << "LRR4IBmgNkDPDi7YXFEVZ4No5G5dJYL3yfsZy4b0kBEplbOoIjYxwz2dXYtX5Wc3hcKzRblKZG2i"; - sout << "GkmTHabzN4BTwbGBxmCTbbyecAIO6MFJGlnxW6tQfdiQbcBbt1utUTpjVhZPVkGolN4VgU/qFPCj"; - sout << "UyO9bO+RUapMvtwhI9+1KPcGiTbQsAX/V9+dSCjQIgD5sLRjfVQcKmK6/R0VSppo3ab0+XHDv55p"; - sout << "FOPkhAKiKvI4Wl1JcKcsx8mwxCoTSchCxp5JhNn+WYBoINpTlmdRKI2hfXvfY+YXUzbATuTLKIZX"; - sout << "IsHeRrzLNmntT4lzgHtEArSwEcYDRXLKBd+L13FZBV8iMX3ON8vUBMLU8QKoSDXatEI//h8RcI2R"; - sout << "pOba7GU2f5TWFy5lB74tBKpcllmmid9w6jE2T3yhxU0E5GFWxWv64oSJDCfyD5GRfY7L2dOVBVwA"; - sout << "H1DuC3NeBQfgaY+DPYFyC2gR6vEihtW5biK4HZoQkEHaBD8nREBdMlh8DcGuXwsTwEH1co2xFaNz"; - sout << "53QpwalF61MYqPbQuFXZBvFEruliv3cYHUgIqtFo902pwFOK447zzj81l+5XzdVZHsA6dCGAjSqW"; - sout << "J//PGJo3M48ERSqeURrEwNN6lD6nOqs9XAkQyGp0xLcv1/EVyzMoYTWazSaTkHbocIh58BOJVDya"; - sout << "rjRytgcV9cAKzYvY9O3NBPvWMBSybUG0weBGTpWXNlydqxlAc7PBND1DfOL4XA6aDHpra2rRpJ/t"; - sout << "xQJvaFWVNRYBOpR34GsrLpczGcf/z5hhR1gpE5y9//b26xf7V66n3kn0w2qGADZz4eL+Y7Wl1rIJ"; - sout << "QXs4U95d6lfp26TVY7MsmQRf1GaO4keltA6LW8XkS9zXro/Ydl49AWToXe7suuJk6OGzaUqJImLB"; - sout << "fI1w0xXDoVdNfY1SgepZyQxrW7PqtQUlLTHccsTDUJqVdu9ZUMnCVlo+6fQNz5lS7wvRbv5iqgkz"; - sout << "DMyynFxFQvzk2L3sZUt1+xTw9r2d7urJ9VmGpj0arjR2+qb+2mfFqH0HaldqN+DGEiibZ7w9PmCT"; - sout << "MNDZvjC0zm2N87yPuRBSbwn4JoAD979lNhFSpExOt7v2zucluinLIqwESRQjWnyun+xTZbu1MAka"; - sout << "JAut97DUpQb5ALQ7TLqKOfk4vSSP07cVRJPSH6K3XnR+ZFX9W+7kb1mYRhJ60r3uKUYAoYJIdGqL"; - sout << "jgbNfvqdTZqUOVq/Sfc2/B2T3kY0W6facFDev+/YnpwWe95pYSfUbewbM35nEZGJ0HVSRHnBTWIO"; - sout << "n7C6Xeg9e29pfohDW3jy7vPL9HU7+GdvhZYMUfNeQTe0zYKuY0+/UtMIuMFzDJ1J9tBy/cLPuI4K"; - sout << "oyPNxmTBGCcf33xcff6ZvAePZPBFgjmbV8izFio89if2qmyhGPDi6LH2NxYGpjC0f+aPj3j3H7Ua"; - sout << "yX5PEPGDl+3l5jZjuY+sLwwqgrUV0skzdcjAyEbLPClOkj2BG5dELl4VcD8ESsOwyk7Yyb2mt44n"; - sout << "GKgKNGm2+EwSNyvECcoEksksg7gaE6ZNXazytt+kRITYczq/v57+U7/tSjyTRL5qPLxWX5OwESUw"; - sout << "y2zx0ulSrfH44+Xxr7ZnI82X5IgDehZJQvNPBmtPTB6JvDuUhJMd+hQF1lboLwEHAfZKpcN4v7FB"; - sout << "GEZi7Sp/iWCZQwtALzUDY4YKGUuS7uOyjpHcQp+hxIlbhXY9byIyhvvVy361/nbVwOnEHo5BaKYE"; - sout << "csaN1xi8WvBN108lpddsUUDRgBW7oKXoiDI06pfubDTDZHSJDABQlnor5sTsIQBMs35yYGuq0lMN"; - sout << "lDJ5h6Nb8r6h2HhenA8tSBmMXoq3j4IAq0jUDpeR9TXX4pBbGfN1HgWpbIrAKmSh9L6Pxa/tB97o"; - sout << "D5seIFPmORWWemSfAMoAs28YqCise3933/HPnCk83PcWH/4S7+KITJx0tIgF8ssoS36XP4J9L/1Y"; - sout << "ym65j7ffiEEDH2rDgip/UQ7utwOHAIW6rOjkComtHS0F+m+yOcdoIwecU9J4rPGLgpq7KW7oSdD8"; - sout << "5/ckw1VAwgUvvo4YT9forakPHIPB9BgroHxUjvbvABXJEkYGw7xUwn51NSj+7LieWTsE+IVs2kWx"; - sout << "TAU1B6hsUPr3f4Z2g74JI1AVGc9KSuJhTtognYLM0amQd7HkR9Y4gmTRYYrSbE1yCWj/gYd5Sn+W"; - sout << "/NUvdGmfqjcmItvBAkDr7lf79aevcKySCPfP5ZzDBfM6aJw/T6EC3KwBpY4obv/Zgx9dLKZhA9Uh"; - sout << "jCKQEEpTnfOOGI7D92zvtySthJNjrGzN8ZVdJHyzXMSYWgHEElfM3bB3LdAe54vVG/XyHag1EMMH"; - sout << "DnH9JOUvMeXHOLRDnkI0RNlGg21wNjl3HTxSiXkIwpANPsBpcoow32KWYqrygnB9iF30IdwfVTbz"; - sout << "OhTNM/4qyrwjdzxSTX4IeMQrviMB+gi12mTcB4G1ggqXuz1q6uFqfxrlmMx+gDAuoEbR0vFF/bXg"; - sout << "M+8PXQ/oKyGYtptl5gM50TsI2CxNaBAU7SUTG2zH9pDkoko6VO3mXfRblwFH4vjJ3XETsr2uAJlS"; - sout << "7wOiJOWfMj9dKFMH6efJkuZPegH2WtkRjomYXO3l/UVkWwW2KuLJgJhAgqJcI3ODJd/kVYoR+THn"; - sout << "IiPnJvoBXfLTKJ6r2lbjeImNg/CwzRVhDVVPJi401mloyrMU6JQ4DtwoqeiS6qAcLDlJMcu2A0bG"; - sout << "+F1isgRc72oPo86rpVxR7oJDEVRSsQqkOhv8O/lVaziMsLCBuqXUGfuohPNE/+mCdSrZZ5LzkKSe"; - sout << "iTlYATHr66c+jnkOETWaEPjUpwQ6ABfit3mbttnnONmDSnenmUnHUf20QyUonJGpFMsWB+DSPFs6"; - sout << "7zI9eOAhqKQh///VoPYY37AdsdacucmhBJY6lmIHHiDyT804IvuNqWLSt5/Cko8t8thgShjeSM8J"; - sout << "9med4U5W4XJ1fEoialm7jil6e/fr23OJJf3VJp8JEaibvAk+rbAbc5VIzwaUG1duo1O4783HLJu5"; - sout << "4we8QCONtekxRwXi5R2gUi//qD1kJKbKxnkYOXaKNWkUbEXPOSy+evfT6jfbYcdk7VvmfA0qioZc"; - sout << "B9nVWevTpoC/1sE/aSX7dqAWjOd5WH+KsReDpJAtB+uMhu4iyzaHV/gPuCAUdm16nVmHgcBP65Ix"; - sout << "Rz93awPYE4aI2yoWBvJnIN/GgUJPBW5rHFcTncTV1LSUMePStawPL7BZaY/V/HRBUOnQ3V+p3xwE"; - sout << "QqFY44ilI49X0t0OR04upM1hjEnx1lVyd/2bSw03lCDr+y6oNwi/hrk389KdFRtlxT0/rItCg+gX"; - sout << "JVV3Q5LyY4WEE5on7coii0m/ZyXMxNT/RitPnLWc2aPtEKhbVOWpVuQGw00eYcKTs+AN1SuqcsrA"; - sout << "7mVcPVIYhQ9/rDYzAzdG3HTcuiFrDWkGIOQp++BZEYitA7zEexC0xZPQZsqcKoH8RieRidrtPNXS"; - sout << "ihFNGNNuxYQWVchClJMvEBrl1ankneT/fJOLTob7xAG+o/n1zdtSeTUtXPN4O4ym3GiubaONLzLL"; - sout << "Z1TzkBa+H/t9NkI+Vp3kVRswJr3cEu6K607OPm6yGAxw/BwpQRBli6uf6SMAdNcAPMwZr9xV7Est"; - sout << "pLz8ibkfNdfMj6fMY9WKJ5CJhajqg0WPFTlnSnaRs5ERtBLK9r8Ip/XS2VUT9/rqeFivpq8OsInl"; - sout << "yV5iKygaW5OyZOtBbI4SrhN30LZZaoP4D4fjXqc5/EzHyzGCYCfgjKrytefR2F6CqUUdBOn0nVtH"; - sout << "Z4xjlb6IBw80vupy3KEjpjsl8eAiYM9JsV9aw4Fd2hjCdeg6yPCNN56pm59Yamga80+31oINYjri"; - sout << "3OcSjN6tVNwdf1Jr0s6Y1+0VgrXT+AbHuMkdPQbhTgQr/AAqHzUr+5FhrIZ7xM2vF3PrqUDakSkQ"; - sout << "P8xIrxYawDr6fXVDWeVPOlVhUSihPBMHjc187YnXDd8Hun9Lww0wUuzOPc9P3Wb8wBTFY2HiXNL1"; - sout << "ciWhFec1G2O1lNNBgYSeclowdwMNrC5z9lk0jhLKLrX2Ji+B5ypECjWGE7ZMNSuETIucCTh4wl/Z"; - sout << "fLIB5I8Lx2D0asU17GjJQk1UdQa9uWdNgpG07osHpTH5FoWxZcQSBl7cvfkqXltox1ItArv9yuKo"; - sout << "3gDp6AgZTFOqYhSdagGzYHdzB6KkEpIUJJlvMZsRzlSNIUtHJ4muh5SbP/X0AAGWnNjNZj95Yf4L"; - sout << "IS5+ZQRnfrzIl7Nvb4KkxbQicPMrtXCcZkWJ0zN+xlNOX4Ph69XZEpmkzj5OBi7H59Kcw6ZB8yEc"; - sout << "3SIw3oNS+6XAIMU1TvhPexpfDTyQNBbIgyycOPYaeA7eSgg6yz/4z1RfNMVZEj8PgPri6IzZc7h7"; - sout << "AzIGqSzGJWAiWCtBFSmDQ3KbDXDMAaG6e8g+zzdm5dnujiAJ+s3PneWlapo5dIvjh4MaL3w6iy6w"; - sout << "T62tjz17F9eEnJD8IM36+Wn13OSPk0iFfPKZfBDZPhEAGRYG7tzc/HKJ/d4m0hEg2GTY3M6pEjZj"; - sout << "nQIcccSE/e76TSkeBNrZGp7lplsixpLjBdRFSFQ59D4juFAU/8tf+MmgtxWd2VVPU/mtkYU9QXzq"; - sout << "JeDq/+MOHtQoMdFuxJvlEj0EE6Aa4E4Ya31LBEoSbp7ln5dDcP/R1LvqaHZr7+XU73GMzpgMec3D"; - sout << "7UY6Rnip+AXpYOaWcfz6XX6y6lLA3kdIsNptHnc+f85kigFZ1RsCXZxugGLjxcFWXVieSKv6PwVG"; - sout << "oQdmyR7KlT+tdjXvfGTP9AwdU3S4QGHi77l1FSZaebpelVkrMgWhcug3s1Ed0/1c55yvaZi/ymXU"; - sout << "OYEtOmPmMAB5wcOagBZsTBP4/6w8Zrfy+27SKh0W1vD/rHQMP083Xsv7HCqWppSVZMWOGJqyUkUV"; - sout << "rBnbjjEmLTyHXr0e5DP7TRCpx4MWMIFUI+fkRdrWSXxNJqPlST0J4BzbQl3XSppj3iURoccQBDpN"; - sout << "VoLZA+61XgLAwY4a+1HGcvVRLzlEhHCmWEaLIeWMfIpb6L7U/LQyfG4nDiMqt5HtmMbLHhSf1Iqy"; - sout << "swpB3hHI+UjL+bFOD5XkCRclzVPucjimVLtsH1QXWKYIcrC23dh5/tfoR4SUxzYn30LEbmcMNctf"; - sout << "ETO3ebcGC9+hvFGH1CwVowGxDhQfxf8tjORR4Vv+L0Xfp7yr0Li/Q8wPnrIQQkbasy2O7PwodnVx"; - sout << "W3iohD3htvNaL90vc5WOf7o0YUKcAWfM5ryT2OJoFFYCSJaI4qLPb/b9OLg2WUL0jV7lYLrK1mBE"; - sout << "JJhgJRQr1OiI4TnIIWrUQsdvkrRVYiUWzVS6RsE9dLpw0ThoXtu3Bi1gWJInLknUCJ/yUPkNqwQS"; - sout << "hKult4TehcOZBfHVc+BOtTdcLNEzQVWy+HPWssvhSNIYtoWp839hdGzzLoFxsIGik40aHU78d+cq"; - sout << "ksDCHIFvnbgBvPkpLxrmvXrKofATp+ywoYFeV0g808/Pl4kX+27zXT6ggZWAO/I9anenVXNcgtvx"; - sout << "ICdGbYeHkfXRr1/IVvTgtN2kaS4sSRbf9LPij72aJoftCIa5EknOgNSOQuqpEDidYdaXZXl+6tg9"; - sout << "lz1qKU3i0ivIjIcaGmxLEi1pBM1LwgEYWcovusXNcv5+pm6SXUgVzQkHu0Iz5MJEdrgsSsc4NN+2"; - sout << "swZHdmviqcDDIk7fuOSwmj4IdjAWUq5lmgYZbpZLZ0pPsmTjqX5uaBFXqmlpKVj/vEIKiFOCGtZu"; - sout << "uEek9ZEpH8aTYqjf+tGKsNNANsDNOFVwLsdD5edStQS0c3U9f2Q1KGKXw16BM7pArxVx6KxFjI4D"; - sout << "LQxcYx18Gm6V4sCn2J0ahj7IO389LWJQJBcfJNyNSFhfaRbVha6itGi8UaBr7Q5LvkCvV01WUcJu"; - sout << "AsuyKjRBScPvjzypYoCxSZp3ln/sXB58RGCVZ6c7UXeZnGs2ABzXEIRYIJyrsNNVky0aGKSHFRem"; - sout << "r2gsZ/RYPQBVw+xt8kGwAkM2km4waF7nHbkN5SYq3VIedvw1gU3UIkbpno8zJeJcwrnoVT9n686i"; - sout << "aE/9ltlEfn/OW7XUGFK4jXB9GBJ455E/9iUejULkvx7iqfRsDnhbI7UsVDn7Q4snN82f65MGUtU0"; - sout << "w9UaxqWKQUZqvP6rX/4u+2IfBKWAksqUG/Rl3O4krkxQRuuOS2KA+u512w/JhgdR/9O0BNG1YuBd"; - sout << "C14QpgMmqPdGEfNrXUZN7uSWJSdBiwqwh+yPFVqoclcjencYDg3ZzKNrfUMun9eKKRBJ2UqPNmJ0"; - sout << "zM06doKb85m39v5GBACWExd6vWsrP8JxcHeWtDkH3Bt6qhZ4YjU38qiTD1avmK12ti8n3lpzOpNB"; - sout << "ObN1g9F9JLpGQs4RrVt5xH7Xv2LsAC3UxcKZMW8nr+QVc9BykqYIU96dVj3kffJlvfM1fTyAtTN0"; - sout << "4016YMvWy9OzdbSeaW9c3ua91Eq0w9Ve7rR4D7rUm0DGwaPPNaCAQP41DDP23U2RkaV2yhcS2ntN"; - sout << "95eArvlyyr0JKWkMochvrYl2iHN/4cv15vog9n/9pUP15ttJdZdqiE+qwBaGA+B78y7kCf4X8dAE"; - sout << "ab3I0Gbc7FLCNEsJGcQlFTSmB+2ccRQtRh74pirUXd5BPNNQUEkZWXyD4tVcDCkWRqHbkxlneyqS"; - sout << "ziQQlbRyiM3MmvSmsUOWYYlq6iKu3rzomWTRukwdFP/LbClcTaMW611t5rXM2Jrl2JXWer6HsRk4"; - sout << "Z65qxcLwDmjokz84nvJ1zpNeDcCq19jnxNqEOaDIpDVQxtM6RY5L96Sn395ecZHJFpL/E1TEHSbk"; - sout << "V9ZnTcZwiga4d4FaVDa/L26ckv1+93o7KzMuxgopuJqf9GJ2c9inY+Y0m+71i6MNharLdfuGkLwr"; - sout << "/iEeyeu8K8QcwNyDDd8QfStXRGgIGhNR573Q6ARI6a9Uft8y0hUYrJSTcaryLZRnV0xYAGAq11p0"; - sout << "YOLr8U2iOncUfz0+Cfc3cu7nytOEpr+jDM70ojkQxjU7DmdCCYqdgik4v093Rv9hTdPEBFtzNNqh"; - sout << "hWlaCaul6E6Pe+PdjMStduOGI8+eNFpOJ7/K7IlXuQLMLcggaXELqeqzUTaXGnMigQeiNsXUhXJr"; - sout << "7g9PJYDPXXLNYIR1TuLPXCc6L5KuF+fjWQ9CwUxT6F0xCBMVMUVHQSkNoqngCeaHsQbIjpkFBbxH"; - sout << "BXjl24RA5TKttRF9mUgNUQK4VV9LU93FJFqPAegUWA8A5AbaRkJbylwWT26qCwRcNcMm+wVFjxdI"; - sout << "BYvJqx7TCrvo5ytBIlgRVx1HLjUcyITmeQ9CCl1j/Tfb4RwslDCgCeW0LHocZde6lCdknTwlOre8"; - sout << "FHdSxxvQAImwiZKBSxPYqsRLXEGMtcFxkpAjbUZjITfHP99qD+h18ywpMp1xQz1FAa6QBaDjFQUy"; - sout << "q6DWqkCYI2cOpovwq+eU9y0HKT/CxiAclgINEMJ26zRPgJDBK2vYmft5gfB0CHj8zUzBuYCK8n+u"; - sout << "6RiFrok3YKqfszQbsWj/M1nBKReS75d282S02qdrnm+OwlbRFmkUX5VNUsI48fffdLUzQAVOD42L"; - sout << "O04+nlesMESB1w0GezPWmsG+eNiUghaLxLnptRrcATVWseeWuqcdcDG9ct3BkyPEjaNSZGbvqN0q"; - sout << "8S0H1IjsnmKrctlK+1ELXGBaND79Uq4HC8NtNseb4gEQFBTj2rI85gXxRTqPwtYvB26mpBPrWmgz"; - sout << "JfLIOawOFX/GEe3W3NelU+CoBpxvGv2wmgqW2quks6TmilBZaQX1ewR61jVhKaI5oG96e4uUksjW"; - sout << "I/cXdAP2GFl20jLWia2m76GGbTimCyffGDV9v3uzu+lpoZ87JgudGUKn/jdJK1uqTad/YQj5t2N2"; - sout << "Y0PnVN59l6Do19E0YwY5vx5vDXs6q0SiWoy/zuY2hqcZ7paY1iJhIaanMAJjFK+3FelY/IH0Xo33"; - sout << "Uhv+4k5pPRuL3AU0nA+rg4JCT9YS3OwOpcwv4kNyQL1xRg2DnlcryXMHHCZPYeVApEruSVmO8nTE"; - sout << "g9cbFMz5nmDHKWdF+KH1Zs9jMTI7tfaOGS5qwX+gBwcMGUPeHf8OMiJ6y0zXo26vHzLJ61wPWLrP"; - sout << "juyapqVcV/YgQl9Ok54s8YGCv9ZB4rxnxjiABMls7ZZquK5kNJ2ShQWW5F9ibtec8EcjMuK4i20B"; - sout << "tTgq8ymele0eOIWVE5AZZ3yD64qqbY7kuCLJG1LxvQRe2zr3FYVYqWhfyRvywRIRFcequso1Bc9b"; - sout << "HKoPDpwJDaTrQn7uhEsb02WZKg7Q4XKZVRXxDGHHcBqwa4fnNM3IPScvskFpmyrZAvR1QrjNLTii"; - sout << "NdjPVn0Klyr0sQlppC50bu1eCy/Wt14QKDUA8OFYOmA5mEjVGtFktwxy9wLssgpAD8LoyPQSxuzD"; - sout << "cgiB2HqNtOwNGPvlE4/ZDfT/N/j8s3lk0q0cZmrAUCBXsBDiAHNbkm3WeDEBDY9+Un6fFF1U5chM"; - sout << "tKKpyvjeZd4bjQXK6zzZNavXSJzOvqVb3OKlH6OTvP11rgv3pMdHYo9T28C0onwMHN53QGPsWbzO"; - sout << "57SmomSDGYs+ERJRpGEVUXgj3D+Q4O4v/fR+XMAtiSOVmz1c3c2y7Ys9Pq9pFX3UF4q6DLIWfBmE"; - sout << "6omLA6O/y/Y6p++EZleosnni/RH3hMH8TvZRYFW4EojNCm7Ss+eyuktlVXQhcPUOuxQ/lK1SUx4k"; - sout << "vCBOk1YCMcl67xok/WdgM/lWJvovLTLqylpQlhsHIM3I8ccuOcJ++lhPABcaXmInXnPMEV9K20kk"; - sout << "d08Q72uJPoU4rjT6SMbBbq8L9UA9Ba7U2cOdK5dr3rUZwQyFZBswroQ7A9cZnuCD4ugJ4l8AnUQo"; - sout << "A7ghXFgGkmzItOKtHoFYz9HmxZa+3qX23EQk3jVdml/8fFh6VjpTK73RPKwrbZyclJs0pyN09eDb"; - sout << "RjZ153ucSgBH0jflZbSIoQhPdmmi+xQBqWV+YjqVYzyYJMJypf5ZrLCb682KW0KF2dpleDJUoX+Q"; - sout << "4pQNhHSZWXtdJTUcRdmM8Wl3AjY0QNsbjyqe8rYj/o7FJaX/Y8b85y1fGLF3qqPJDxZQgR+jKfTg"; - sout << "0vlbJdh3EbV5L//jZX7EcOTDU+dvkXOSyx8zQeS+5xwVXWPmTmaTNIriV/6EvNJBPQ0vmYCjsUoU"; - sout << "6hD4EOcBuuOXADFmEcgRZl9z46qgDwqRasacBLwpaICbLCnpc8Q7QrBhpbmeHsWmqYtK6SzfiQ4j"; - sout << "e19bNsz4SP4zzFzdEpLl/J7PeNWURM4SUBwZcNOZbDbD8as2KcD78JSCs0sG9zWL12JPjZ9lJ7AY"; - sout << "lN9vqJl+2N6H1VGiJR3eO8Zrb/lYX2LkSz87AzRggZxDXdv1DjnPzGj740McdjWa96DZeexzAKtj"; - sout << "DVHwn2PFsYDKzvmwr71zUNYLxwcK2U3ayJf8nuuP5nPkRDwl3b8ttN9QHNo2JWabnVJKWid0Dmiu"; - sout << "zRus2qOuzcXLkGbgE5DdmONYcg7qznU3ostY+QyHO4/UZbDpqOPG+uXuk3SVhp6yBEmO2yE3T/We"; - sout << "WCWw3dfW4DlOTxb4m+nf29ST7WIBoNR8omWSyxyZZodAXRy6NfOnpkYrgFAorXCprqzNiRLSbi8e"; - sout << "hZrbJNNoqEUTrw1R/hXMJHftJH8GotFVuFuXTBV0wcm9eM0UeN1cWvuT/0dc7ORsLdbhNW7X9Uke"; - sout << "tYEHwobKiM0mfa1dCdTWFvee2XkrmYsNjHfMNoQRUN/w+1VHqnFy1Q/qc1MoU/C6L1rPUjth3gNC"; - sout << "oNr7jNNWH/tXMAEGqsHPP3+Hw9pqk4XE/B3QSbeQYrZqcZojBhWcwLJQIbSyJmya3w+QYKqie3k4"; - sout << "/LNyngQb4on/sr1vLNCNc4c8yJAxV/nMoNx5cVDBVg/HNAh+qwhQ9tVhi7xRUpIrAPICcaWaX+RH"; - sout << "Wl9jEeq6PdI1bUaTdBvF7DgSvAriB22oHxG6Jy8X5WIycn4FHMF0/ZlCfwccg8HcjvZzDlvFHfbs"; - sout << "lepXFAJ21XIOWHwDzG19VnizLorKXM/FmOnFwClhG/+yTREVbWCjaTkDsOlWL6JGOehVHckhxWRM"; - sout << "03eFtiNGh2k/oqsqHkDxYtt0rMmGly3vLlLN82Eiijq2iNo19EF+euIVAl2h1iEmOGXQf6lkXCCZ"; - sout << "yscrDfP3XEbe0grXP0+/ETyFrAAl3/zoENKR5MUYxVziTQ2cy733o/8aX8J/X/hFTm3ZFVKKmZRp"; - sout << "vS7HzVzjj8i9zZNshaRzWt1jYnQxKtJ9w9BEn0VLGcb2spLOKyctfmsughG6DDA7wjjkmNDNPFMH"; - sout << "mefa7RvWjXog4gPP6SiKITag5LwuBDotZn966sOaBOWK85QelE/NBsk4hCsf5LNPkMDFJYwB2ZUK"; - sout << "g/+WbwWHIGFI2O/sNsM8W4xDyia/k6ZFpaki6uOZ48uR8uLOu9mMR7NSJ19gRoe/aHeOAi40FC8v"; - sout << "K6Bs4rbxF82hbJmSfp64b0d8pNPevQ4X0UEbQI8d9o8RjmDqgVwV49InO/hobuZNWyY5sIz+8b+0"; - sout << "swgWi4uhT8bsQvMswskKsWmV9bxrrQ4EJFOGtzCIeC1X0Kzm25gSf7biU85dr8/3dXDQcEqdL3x/"; - sout << "BVrjeWsXd3ko8dQTJzD2kNLqi2yNmymIarjj5qzTxlnAZYbJFtFxtPPO4bfPDeRRQ+D5PhiZVZn6"; - sout << "a9WUtuDFmwKmoZKZIaKQfQtwkMQq6F03sU/EsO5UuglsfN9gZmLVZrNR89YPC10gM3cSXmABMYcx"; - sout << "OHnaivD81i4KmkX23r4rltxlqsgzdUKiGvEpPhfRwD1bKlKb+dTFgA6x5cYaOQ+2/KqeGn0JvRHB"; - sout << "HWmQQG0aJvlLelva7sG2mahqaTpsRGunwr6EkeTDwSzY711r2cNcLBRq5VGIg9ODw/Pn2eMN1Sza"; - sout << "0xBt8eEzdGYywXR7zpcalcJfOOKUdpm5D/Lr0Q2y9qGhqr0yambYW4ltxSreDuBWLTp9lxjbPmeg"; - sout << "tpdUAqTqOEIfskeG6FVSzfTSzUN+q9BjZw4RE7aWCtqKm1M7hF7o9FRLMhqws47tamk3AZZTIC3x"; - sout << "sGaCMG1/h/gu5bH/VQUZpzIj7KWgf7PnJEL/WhAsjjgx7XRcEW4OE02pwmhe5C0WQehHYOdTSTQ3"; - sout << "Y6djpbeCYLGJNSW42W9N5Qvrp3GTYyqDlRcFgsZFDK0DmBonUAsSh6Ytr8pxPSNWAejairTkKoi+"; - sout << "gon4K6TAdRrK8VQSSf+eWE9oTlcteftAn4iWQzY99aisuJP0MzOJr6gZgp6s2GlsaiAA0KObTlwm"; - sout << "/SptTPhSn9K+d0Os8QMMXYHlhF6waJ4i2NrCiMulOp0vHYPOKmfyCI0+hQt9R3ArNvY1pqhwBHYo"; - sout << "+PMNqnEJOH28aU5s9HyHPOkQOOSvMTYnUo0kOns0sa2dSxuaZOq9kb7aqV4qG6+ZXzFL9FbAhth+"; - sout << "4FWqkdDxPUbYUh9pKNQucyvCOtJdlrQbrJgMOTHShadJl9g+eF5boVBDAMZI5WP08py+U2sw19IE"; - sout << "/uju0H1VxjRPA4xips+lxnZdrgWQu0zG2nHeLObhdS/gbO3R25LWZxUILdNWpVbxuQrE5dRWlIaj"; - sout << "aB5qbQO7zrwCwjB/ZjDc2dNEH/4lvPv8vnEehJBa/sIoieseVzcQgFRLK0n20sdaXil9vJ3B5qyA"; - sout << "UUJBjOT9I8dtvWAP41Y53UOjkkuqWOoAbfd0sp1wxpmjOgzm3BhPoGmLhJRlJ58LkVYTF2Ix/Hwt"; - sout << "ZvLSvOwNE8HdPFOU8BALTzAPtVpfQCKKDD0iOdAzDJC8NX77Ar/8UGHkgcJP+9LiycmuPnxOzhGa"; - sout << "tsZH//NolNfZ3+4HA2wir+hHAjw0ia6d7OSIJyLV03kjAsL6I/CajovAsT8uAkegs8ydmvLryqGB"; - sout << "+98uO61GMTuZNyrv6TIG0oRDpNiKywoYmXoqdum/Dj2UtsE0A+XAb9KK8Nl6RIM27j6vkFWjgC5p"; - sout << "7Y9u+TIHFd18fxQ20A8RuTKooQaC12h11+nFUR2kAUqkJq0e9LWklG9jiq2kIsfHr6jORWi5b0a5"; - sout << "Tdu2IDMtMCS/58L3+RggG0BAs+hc+uX0txQJUYEUCmJDlzReIAwTWj/5j6YRFpq534fp38muNl0k"; - sout << "OV5OE0UU/K+TLtfXsUQ1QCbKOvEW7RcDICz+5sC2/FIGC0B+5KY8GUeQILlr33+LIxJTKAQwdp3y"; - sout << "G5BZk4EXDjoLiis8OAO6TzgfktYXL/KwAicyHdRzSUAuzQ921QA8TeKMAPd+4UrzhUp3S/cgGxNF"; - sout << "sF9DVFCaYR7t8Wg/ecyZ/Kn71D8ClkIigOWuWyN2XIuHgLVb81DF3gDdcoiMQ9qroTmrCbJCukft"; - sout << "4zE53jcVbaVnhr7hK56cEbe3F1eFW6pEUFNLOxFrFOwyiwngw18cy/PUThn6kpenQoYS5f4FXPul"; - sout << "qlFnVMTXc/j5mWhRSNXddfti53WsTB9eh6UI/MSWNE9z8kjdMUWn4z2perojF4VUavHqea9U+6ms"; - sout << "samqyF9mL9CxEhDNL0+Uof3C+yPo4KSqMC3or2rqSe8IyrAbemjcfKGqMjsxBpSGH0YcRSNoHGw9"; - sout << "wC6wJ37LuOncQ3VLJM9f8zgTpgln3y5NnKz9nMrgXE017K+nW+I/U8o0XdPAJMgXlY66pfkJsShw"; - sout << "JmidbowoBQGAXWO35cYQ92Avtitrs8kvq0GIuUGdIsej9PogzuW1ZLTSNJkee/S9t39cXyQ9YpzC"; - sout << "KSj+H4uhCrGCQJ6Wts6l7kEhHG24Eu/mwSEw0fTTNqL5maeimxJsOdmR/IEVC/pps38ZEUuTJXuq"; - sout << "J2uTsoZmiVs3ImbIkjHnN7NF0I4ryYf11Qd4ULqa3eIjraoiDn1KOZIJK41CkKOYAYIts6aFS2iw"; - sout << "+34SlRxtIsIrn29zkOG3L0yc0o+fRT5gXHXPKv3eVmNca7kPGaqSrBzv+c1Fz6ShHK6tBjiPVtwp"; - sout << "S7k2/05j6t0tMny1O3QzQZDTdaAi0Xx5yg+1oyK3M8XrgLsEU5aN7XNRorjMSHv4XT+xhe7F2B7V"; - sout << "//JUutejh1StxytV/AswArYDSz63Q/7Vs1Eel0/Fkc/Rz9gYPKDqYo0UAFi/baWoUrSYZjYlIvBb"; - sout << "fQn7nAgDTWd8Ry8dx/j8kUSR/CBWx+xz+tNPRl0TjIOWZJtT/MOqLZyODDBisgCpV2en/f9VUr0o"; - sout << "V68sh5TYTczlcSA/lngZLj3uNH6n5Vh7u1kjINhOiYzYgaUzbruQJycDF096vEGTZ8JWnz8PNzT+"; - sout << "gkDfWt8JtyCsQg3UHfO58YSspPbapErvM6zc+nOzOde7dLkjj10pxRtQMGc+fES5ER9QLpNF8wIR"; - sout << "zGF4ENLZ63jytxO/Y3klAguefc14abKtLaOi/SfWseX0HoNpW+cA80GVq+oYU7krKhh4n7mg7NHX"; - sout << "PBsUV4gmzhj0DkVAgmaGd9qT9KlZPWHU4vf1pGE5P8mIoDIwWmpFsN2sz/mEcugGWAT2JhT6CP/J"; - sout << "sPOCAB66Ln2SoLzSJvBQwmtzdBZm0a0NF6TOloCdC18cJZfqYFuEJm2gUozzFMPjLKonOR3Zhr2w"; - sout << "CpBUdXL90wQKDIoTIjnOIorXzjFZv+O9O7y4OBDpjDsFrcdQG3AZPtHcQU49W42aH+6XcOUqlxRc"; - sout << "DYsJdg75GZliymNIWgfGGuQxJ5kysL8iPjmCxVGNnXM5FvYW/JYzcXy4A2DXqO7eyVBw6KXKHxq0"; - sout << "SEF7IIjjj0TgUp7tLKY7/L0aIVuvlNblobJNZDAHYheG6OG4dAAnwaggRO/dneeiQ6hDtFbenFmQ"; - sout << "SWdiYK09ZQIpXbdlXUlPiJIG+mtHudHcbaaZwVlYS5iG3NIOx/IYdKzYnStqv+5NpkBEPySa0sYy"; - sout << "npsOyO7Hz/09SMXbSOo+RBkLYrYXiLtH9qiSj+Lv2N2ueJw1fN2PeCd3I05jcLJwqCEsZ0ch7FfZ"; - sout << "1MaXVqdKYBD3BxBs+SY3qtHgffqUrGXDIbFARc807a65++J1NLuoD5ZXI4empkMcD1O2j46tQqdt"; - sout << "MtApg+jZVUSdVCJl9YArIMwOuUdgH0oPcOItEBlmYRmDDcWX0E/fuPfa6p7u4X6zwQNY5PdI8nG9"; - sout << "+Qp+PhYXKt4fB1xEivvnHXy1x3xMmDwDH7qyZEmvpiNPxZSO5ZGUbDdzUH5AwTCxVRA2/Yd9B0GZ"; - sout << "+WkLsX0Ds1K55I2cTSN5Tcb2IgT2VQnFb7ceok9bY3ABeIipxABdKzhLiDeYDfeI3is/pftpDxGC"; - sout << "vkyX1ZUnB7cpC9lwlwwbRdXhQ8WidAEFZNmt77xdTVeyU4cVby6WCBelfRiKd0+wXtQsSp8s7iRY"; - sout << "rNHSq8FQwmt5LPdhIsvLlD6254b5AV95lYKnriWhCJ3frsNaqWc001OhiyRT2FTtYIb45pxEtFw+"; - sout << "fY3kLR/xs0V7KWpXJCyt7uI51vRNCIlI2PbfEe6epAMg7oLrbXOtjJ+z6f6C5DPlVUf5pxNNg7PT"; - sout << "EEGuVTVEkWOD/hKQB6iJfhLqIbgfOnglwI+pR8JzGJSJg+yuK82g9rRvzhPvIQm8Y/xmsMoQTvg4"; - sout << "zBpA2kgegUxYS+0VXG6ZEbO9CjSdmtUvcIpa1yw6emAmjR1TgiDqtTtQVWksXhRNiQ2XBal/YlBE"; - sout << "+45tqfGRjSiInj056XBdqsQqcQVuXcxXMT90/uslRAEV7oTmVX/XwSahsuP1VIevdVI+rC4liNj8"; - sout << "yWmDEdVfQOM0u0MgevyXmDj8BMQHznlHulIVNQRhEUmX13ibjVIDLBHDyozBbvPneuY7hBejxnV+"; - sout << "0kaG1VGjxSaTpH+jOhXwK2UvlXPTN+FM4UPLS93zEzEiJ4EWDUR1wL9VUXEfx7IveXQSYixy/rMy"; - sout << "zpSX+UpoqFyiiKaG8DGxg2hu4CPjNXfKVI7/IZAFIT6KHcxPrrO/3BZ2HTqwpPUpD7SCiVQkqzwd"; - sout << "gA5zjgo5/vrSFtah1TLBrd3PiN2RBsGn4EzHBdR2IIrDQcE3uB3nEQRyWPhRcO2pkVry8RtcGh4b"; - sout << "hkH0XKI26GAIC3cEuRzdwWU70JlrbuN4beXlL3z3xqxz6J88vpg599N6Offn7V0Ki/z0w+kIgHJN"; - sout << "H2CSc9uBNeKfQMGNdQzutSckMpg/9neWQVhgnegiV9hJ+L2lQODdnUCJp6nBYPHqkgtbOAEFRL+K"; - sout << "tkKQcyc7YkJs20WUsItmeW36T26+0T3uyN72dbmGqBaZIonm32MKPgh+sVTbUspUiLdPGSFz6zuF"; - sout << "89/dwHkVzv/dndOHh5yb+itB1LaCjsDRmdHw9uBYto6eJDdPa+XYOBe2w2qLyOHph/3NaHrQI8ia"; - sout << "gfvPuVAZ+V4XXQgXAaWjPF0XtyBmRuEm+C0J9nWznqrKjja+HXr+hnmlEuJrHGFIJeTcpNrrbcRi"; - sout << "RBdh1oiBTCG1tipAQeDf4fqRy/zrvugJg5Rx6/4fA39XHXW+IjqLMuOpA1qCpOWf/4llleXazoM1"; - sout << "J392TqpJVxxg43Jfxehz+XXl//IZPMCoiNeuztiSaWuGsw8ZjBAc8mMnzehBVPv28axiOixuiofH"; - sout << "WF61WGuAUXWOZ5Jqsg+7dMuHhTZznTPwGVj6hzw0cyL4KEoj02zic8JaSnnprKzhM442UFIADacs"; - sout << "m6PchnrjUqfG3D9DwbGIwwmVFXePtscyV6vb19r5nITmXaQHm2zJdkpVmwe8rB0Vusg+d3Crpxe/"; - sout << "kOWVPy11Gv/rkYx/1BsKd7SQJfTdsZFABUTjLIaJb9pmry7FnI+pNxWNfIyEu4wHTyNSs8ZWPOlM"; - sout << "0oXY2CqzRqdnzt0rOdEp/ncMhjN0p8q93nMhfwSlH3Fvse9KZ5Vrk4Np8HDTOEjjgCQO3vpDveL4"; - sout << "S53oq7IfTsp6lKSyzR3h9iYqa+fnr2G5wDoIlLBPIRXOx9+l9OH64rrNITKFntHEkFFJi8rnkr54"; - sout << "0892mQk/ia9ELfn7V7pxKnEgXnSqDwldLYAWSD6hhGmvUYEvEKbS+4/D/zsfnauYNFkc+cdtPuFx"; - sout << "uo6oFl+6Th5VYBwrmk7xq7j0vu9xxY0NfYXkRcd2gp7tuxAtMn0gJVbfu+IfKW7cneAT1fszraXX"; - sout << "+qVo8SX9+d7phb7a4srtpA6513DcZSBSObMJ9RT+HZeYDn+3l986vZGAOJoQMGIohkU79sFTdqfR"; - sout << "i0lfN9X/JohokHdD9eroMq/F4vMWEDj4ax+OR1wvJ1T9syDfS7YT9IXXt1eXYL78aHwoCJ352IQf"; - sout << "Rv3phfVYK45OdBn0BqAQiUhWdQxPcwuvt9O4cs1JdChTjSSGz7bKtOPTBxst3pj0butIF1ORrs/h"; - sout << "n9iLG4GVA///xeK2m1OFKDuk9AQK8hX+Nm6lEu5zKZO6NBJjSzUX928LJ+Wx93h6F/I2sy6hmBNS"; - sout << "0bhas2TNmGX2Eiu/EslEzPudjbp8uBKLCyHvM5P0kiOdzOqfDD8L0qyuOLNumauOEVB4EuSoROZg"; - sout << "uGPoG2xyiVGdkKIXIfvb9YjLpU3mebY73zeInPcaQCVY0PcbvEYIs/wMXch9CU8FP4wyJRYsp5rP"; - sout << "XELbGL9uDF4FnhmFDjVOTX5j/zV/jVNNWOv98Ue2JkVZ1o1gMweglPMk5Gnxn+twxl/OY785uZkB"; - sout << "CJdsVSNbXanSF4dFQWhkEsmjXyA0PVsw7uD0tfIaHB/BekjR2hWcwbUmsAUzIm4EVvepSk3Ec3rc"; - sout << "v0ZNE6FO3KD0kNueAt8RvhpxbO2AOL5pm3vCt3q7lbz86dalSf4mFuv23Di0Dyl8iZqNAHzsrToW"; - sout << "50Wua4dgrUEhqSgn8ohZq5Y5tI7m3uiP2LTLAfVDag0TdA7IAREMvTT9F3N2f/2Ff3j3b9HW3MEI"; - sout << "2PU2HZOvUzJyyOfuc0TnK2DXpyXwGWfgoeAQ1X8c4CZhEfecXs/FXM+BfUyJEBAxnAkHxSV2oKW3"; - sout << "PPismlBkfTS/XPxnChKJu57uy88MTaL9cEfVexPwEiCWC8VOBAd2nQUYouKadJLVx2UliM/dKQu2"; - sout << "oYzUBJKlphHkLMGgUUuhXmGFa0KFkpisUVwFtyX1ey5myWvdyjm3xGkJJW7+6KzKIuFZ8oF6QEBS"; - sout << "thWf7/v+I+TMG2FI3cl96g0zhRnguGVWeQ1NCzPRsDmK0fDZcgnCGyQGtQkdVgRBysC5hLqgnq0W"; - sout << "+2S+oRxhL7AY+DaVGOHfYnI+7jyf+NVcHhBfqw9qmVH/rZk6cuG23Lj3nS4IzdR96a70EWufMtHo"; - sout << "Mu1zlfyTEcZ0HgZ8vZdaBzCDRT7nkqSou4/uomjQzFUBRkZYD+etcrNb70zi1Pa+AR9kYXVJIO04"; - sout << "TTRLgikAT4Ja36SlxjBbWDS0swHyWy3ecHhdKwVvZcklA8yxzLvdBbiANv51v/o4O8vP2AAqAQV8"; - sout << "AcOjIeo46sslQzYC0aYgaigzGmsx6nYGgQ2zFH8bpWR0KNnP394GdKHx0AuIjmC1/FKtN0KVVQeE"; - sout << "xcU6gbb8Lbe9Mw/WZYDiGtSVXrNBlD59+0dNG/SAyDZj1LqU1Fp0thu+CzID2LVAJtA7z4Wlx8f3"; - sout << "t3iMWYIq9+3fDuCta6hORK3L/FUG89sB3Rqbi6OaYW7nu60+tm+qRP7ECwoNcyVFtGLOOMDonoyM"; - sout << "uHGfkqQcEeQaAfPeWl7sEbnzmBbdqZ+xUj63WTpGQ42Ceqaa+LeoUJT3zdt8tDK4Hc9j39rZh0zX"; - sout << "HtwdVVVlozsSQO6HvvElDrBwO8EY/YPczI4dUGCc28KcHNgEQQ+M7UenP4mbqoTIbo4Q0i7FCvEu"; - sout << "9/TrBv0JQ88ae2xds6Lk5xsQomtH4ITr4VYliRfDO9bD60zxQBhhiHo5joRQ7sW/t3ms1tDzsco0"; - sout << "YG4xVvyLzUHdDh4FBNVXJ5ZHnHnTBnppmNZ3M3ucVQpk3mEXauKEdPt7AWwdX6rCqNR81/ZasFJh"; - sout << "qTU34x7ZH4Lg3Ut+95CHI1qubhh5W9feqPOLbvUaJzuqxlcsTZBhY43N1PSCerMFFez/b1/Rtw0m"; - sout << "OAmNLDlwUfsqtrs4q3OnKlfXyHlwuIDe3fClgcE4r+9i6QSjVzPDaPfRwE+eTFRKPm9uhlLQYAnP"; - sout << "YvVqOtnUCbLQod0SIakYVb24cwnvuHfGwx1tikIizumhn07N/8LXxtVGth+prxi0Lu8b3hMI52e0"; - sout << "LXdzbJfXUa44mXoyEL0ARMyhaombmUt6BX/HaBLx19n6j+munyoheRlpqIdIMWllIAaEMbxCoyXO"; - sout << "ExRZf8NPFANOB0hvtXhN5U17hleB7U8ri/6Uf/j30M+jglbucV015gMXP3h+E8qg8aOy/RoxhkWi"; - sout << "lJBmD8OIGytd0GcoSmpzYzpoOQ+DVNMvVlBJxzG2sQy87PtAiJW0u22n5LGUVEQLxAJrRDdEiKcO"; - sout << "zp/wdKOlFD102SjWbLp1mQIrqkjVgGRs+M8kIzTSyUw3HQPOvucyewaf30EkILRHwNOpUq18WxRt"; - sout << "dE3BXUtm6v0Xc1rn71+Kbka3pXmU3qoLLQaCLLFQXcDBnKGt0olk84XYOb0/cTlnxBJiips2Q0z1"; - sout << "CD8+ny5LOekJtzcze0PcYimx7Rl1EilhFrq9fF2N1cdyF2skG9UfoPVor/6U4dDGzN1qTzN/uflE"; - sout << "harEkP1c3/Dk4BqYx39Vym1nvTSs9dYVYli+6K9EPaxjUu6ng+3ad48uZWE0xA5i8DfEYU+VBYSg"; - sout << "ssaHO3OrblCccri8HgN1U17xnDR5C/zBxpcgoQ5BZfs6GYsjsDmIHz4Kq7dCvZ5xXStqZ9FEPnNf"; - sout << "oeAMmWp6H15Ci6pqj12u9lZRG4rj1kQvaBvnpKgWOiscli6Q6mCTEsgtIYvmQhds27mbO6Y+uwDd"; - sout << "ZP01VsjBxmPZaLxpUabmGIZiwAsAZKLN9qQgRt7EdMIDsyLehadWOzcjiYTHrWTrQQ8R7zN3e/eT"; - sout << "NYJ1wsWhxMslZ+Q96vKEmejIyWAvcVmxkCuiifp6VfXCF8BEYRsf0lY+Y1VFe7yW1BaOfy+8Q4z6"; - sout << "jtBh12vD54vp7dOo9xGlFYZD/w3KQKdKyVnnqm/tbT/pCrc/X6vbrgsf7sIbV7nMUJ12FRKYYX8J"; - sout << "p0C9T9nkvrPZkxNhUKIMkra9+NT4tMsBPw4MyQQsMmyUjLjZMTSRiIyupPjo8U01tXEXElUdaNxj"; - sout << "feSkHmcQS1PBXV8oQfQnwEIXLGUX84fe6q6i46YrhrEhjf0crtCdpYrDOuhJPAKnu46i0sHS+xRG"; - sout << "B6/685zGr8K+1ET/1RqwR7zo8t0vJf4zqhAY8OpWgvY5ASN2f0gK2dzBMfuqrai05tWaJaUhYRqL"; - sout << "7FufcaIyxbmDc1laARn/mvN6pYA8C0VxexvsQqF0OlOhpLOsY31GPy722kMJeKxFLuFYYp9DeHQS"; - sout << "Br1KtYMU2S/RnQ/sIbzZ15aSbYstM0K8J+9hxHCi4aq5K7KGzzNCSsuMTAz/LEDEV5UKCFd7se7E"; - sout << "rs4gwaKuHqH5b9n2cAb1wMpM+VMttHtz5oQplOyrZaz1r5sUV2KGkuU4oWHiHKSgu/tvrBvU9LqT"; - sout << "1TB0RV405rPe3HjtIAzSPD8/VUHZy8pNBjGi/vqZR/ybRNP8GQFWZZ+vt6l7YMX83DqWbTbfk4oR"; - sout << "C06g1YnZcYt9UsV0GldCGJQZ5oJ/UHLM4bfRzSaKiRAWTwOuDv5hHL+AKJcYOvDub5pCp+EsZh7h"; - sout << "kMS9v85LQNmI5vUHbhrS8XE/2Zuki86Oms0AtR3aVWWGWM28oQopU35Ayu0rQE4oexb5NDjsKcP5"; - sout << "A38n8gS7O4cbHdQ3XAIQaPoUx6TVK4xL5Pc5bUJ5aRsyivvPGd6meP4AGW8YBKObMMTL6TSRBD3R"; - sout << "e0B/yqLXd30yRNiazNfw/aajOIAeHfbNuFvNLeqe7IKdpRrDyLg29Fh5n3Pg4Y5+vdaJigi2Iy39"; - sout << "FKvhs6zp5SE0tJubMJuFDUfgYqmW44XVNv8z+ZUIJCYQScuSkCfMmexS3LczxvOTqTm4yShuNr5A"; - sout << "+o4wXi9n6jEa0pcjTVlU45mbOXiX5ELwfbSpYDorlP2BJTZqMAvnfMdxdX7cs123ysQUHxMqKK/Y"; - sout << "fSUaJORhU5zWNpmiSFeTCsH0uVuWMj11H2ttk1+gotSz7I/RCvotGNzuS7SqqOZzb3h6LpjHtplh"; - sout << "04W+r6/FtyAq5z9gOphIZQUhoyO2tvbY7aCciCBBX1lugJWKgCQ7Ui60G80225lAU4b9QKOGzwN6"; - sout << "hqKtxP6c9Dh+rxiERh4NoE6rctk+CfBoCAxDOEFVM/bcWlEbed+KvM49mqrDF9epNJP81oIZZb4i"; - sout << "afVphDQaM8LuV9LNhPSMGLOoPeYNPAxyCHnlNyAsOrZK7otQ4lFc4dLM66NFJBbSA5iBTYaW6MqM"; - sout << "qGCTCWxu5MzZbgNgwMv7QrTgPy4LBH1qwST759B3Um80nBimBa+8HbddXPbsqC/KzjW2yjodKmqx"; - sout << "2t6vnINn3v5Q9dFFKv4oaetAj2dqSfbfCAvcMvS/Tl0Ont6PZt8iFpH2NmwcEbrwSY/2cKKqZIh0"; - sout << "sVj8a5fxwXHAkMcov5J692QQf7P+OUzmkI/P/+TlBoWYW5Zor+79wh05NnrDHqdVMY24aSRKrdIE"; - sout << "qOeME0ZEnPkMyOHJRUzHoPPw5+r7Oowvbske31mERKc3Es0cCjhJ3bab+OSaoXt2xfzgLd1e2eXm"; - sout << "DtOlfyyLT4vK55WTN+IP8xpw+e+D5N4bdGDn4UC13jvhrviBYxasYYU/EtFWpFGonD17FPZ22PAX"; - sout << "pGNpKbM9Eet+Cabzi3hJjAXwKYCmTXHk8+aHceC6DtWExR06Dj1QKoxSJg8vcbuik11BP16e8fEB"; - sout << "UiPiV0hzsLwtHdbg7uZQgvd/e8qoyivF/NqZ9e6A0s4r0NoRO+6s8OPAL1QTpavvLtDhF37g+7HH"; - sout << "vjGkDbTbzU2TOdzl0OvxmSezNfASeloEOSXqIkjMStawtwC+DgLRXzMhACjjVuc+MY0D+/zGt7lp"; - sout << "hQlKeLuLWLDEUYNn3Dvb7G8/6beGyyjKHlHSOr9WK90JsS6OQT+xkEZnOibrIzgZBwmws4SPrhuf"; - sout << "wwwQCiHH0xjOXF7iT/vi5qEKbJePKxh4m6kbcxxaPmP56M7TfmPSYJ62RTfZbnq/FIPurztCgSHG"; - sout << "jmTJvBi4m1qzKgiwHVBoWjHK3wnjQxTuerxN6CMuTA8mv+ayvrabdbRwpVRJgjSXbb/Vzgj2e0PX"; - sout << "IeHUocfZtrz9J0D9f5D5nhIlqv/aSodiEn91GhsasFWwDWwSdvKwAnTtCaC8EF/+rLx/qzCneheg"; - sout << "4gGRJ/VgaQ4ib9Dx0+0SnOEWoE0GJAceSDJImKJ7yi3Oo7Dxy3J/tsJFST+JKNuakqFkNCnTetmm"; - sout << "FkVNEXbo8tKzbeuXlextGxSr3ZCyOYfxXsfNQUJawLDxpL6fVpaDbf6Ot7ip2nejQHL5wt1AqQIA"; - sout << "GNwV3Gszf6q/bx3vXKCTrkL+AFc1VmJR4ZShMwVlVz5pdzMWJWmal5ivqLFkRQ4q4HXA1bPVhS8O"; - sout << "tfnYwzNTBMRaNMpsKGTL7MOOHdmXUHDPKhoMki5bX7Oy66C0x35qEGyTozFQgygLOMFe6eOleXF0"; - sout << "D40Yu6VhauEQko7l/VN0rICXaTJSaemFIgmPLT7lMfrd3Ta6XNKYxXB449SOC63A4doQBIFJnekm"; - sout << "VO2t09QLePM0/ztdbA3toWuK8cTSNZgelZRbSlpgE0+d5rqxjlxmKom9I/8kj5bjS8FrQAbbsnR3"; - sout << "pLEArb7kuWg3W3Gc0pdlONrgWVvoYpTtwQsrY8QfZmIVmQ+Dkcz4q+LqfYRU+TEIM4Y06Gm67JzA"; - sout << "ncMkUYcVm5YS2Adqe/VFsctKTliHEPhPRFpo9Sfn7csGTexvc+IYB1mFIWy2DjRSsfIorQJ25wAy"; - sout << "FX+E8PlVJPf7S9IKNM7NmNX4k0aqJYJt9VVuDidECJrYiTW9vsJ/SDM39qR2bQ01w42aG7/ksF6B"; - sout << "tWPS6j9Gr6KDOtdAK9UOof2Oq6uI1ENoajWJuDMxe9fT3J9/1ruJMMDE4dn8eSXCi09ip7qbnrxv"; - sout << "5splSpCC7MvfBr1bpzbQ6gsZF7tPXz9R6FfM1idioIEJITqqaaYdABv+qwad9Pwv84maHHslw8O0"; - sout << "/SPCYyCpfOAbbYuVpw+cEryeS7RD7LNom1M2Qo6A5Orh6Qmd+xTExUOJCxkPNvyxUDezWtVCPxHk"; - sout << "lu2vn9lqjj3UaUjALvD9xB0WsGhdJbOUtlQw3e/gEloGvQ8rLamwCBAelQqTTwM10iHkvx/AzA4p"; - sout << "TZSpfZOyXY+3HEjROeej+gvP6VXL1lWcYSrEexizEzy2eN/H7kVTEgnHOzYwp2A8x6vlKHWL1gnU"; - sout << "lGV1R9H2bzmhj/iGFom/TMYWmiv6mtUe3PTRTIo3W2yjlLKnBiBFhWMh8TjfcLz63dadzCLEV3hL"; - sout << "D9DPn8fnJYh2HZyd/N2/lVDtEDnxqyd8C5lZnxY28bew7dB/n5euYTo1H6zWvpIHlbbbmXrHd8VQ"; - sout << "iaJew6T+G+bBxuUpUQlu1NscX6mcHZBoT+/g8Ng1Q8bmjiT7/c+Pl7wpRlF/0jwhWxPIL/5mlmPf"; - sout << "+pHBfYj5BWXTDrZNchJKF5Pvwdnl0GbmJsPPiQQ+Wvt+y3lt0wv1gwefzFdGhi77EJyRY841JGSh"; - sout << "LU/TVYCUFn1MdrtVLhGdomxo7Bl6Zj4bZaYlKXf3Qru1bRjD0Ug2tIePfjX6TPymfgxBkgGQ0zVx"; - sout << "dJuLeeRNmhIFVH8T5jJTMcmd/lS9u0hzXrEjvpSFKPmH+Fmv6LJF/D0bl/V0rYEI2gKmpAWJLd1M"; - sout << "iI6/XFF/bt6tQTRu+g4WgPWk8HG3VffsHUSAI7jDK8hjrwrJm5AHIa1a/PRxiSCH5Hi2EHCPRfkB"; - sout << "grfcTEwLwHJA+j0zK0tV668Bal1NBw5bJGMzQSONvQqB/vnVqcRDxhCyztjW4oAnEeeBKPlAmAsy"; - sout << "MlrkLbkVwjiaYbpzkYCsO/KlIAgoahAJEOLghLQGBuhP0voDuN99bKp5JZFaGlaaycEuYFHyk9f7"; - sout << "zo6Xfa+hhmjfgWJ8KCFKKvr50ndwdqRTafQVxjZRe7QkwYB5ZIpwYehKsXlr5SfTLp+ANmZqP832"; - sout << "yXRF39OZLGs7xtsMmPdI6WX/xsOKiCc0oLHdzH7y7tYzLVQVSFdRZtN7HA2r2Aun8gTbLExrbfRW"; - sout << "+16+go4YQEubnMwW3FeRQqbpM+0GCsiuTlBPTgnvNEWi1n3JHGHeJLs/0HRNo6ba+jVE3vIZO6B8"; - sout << "NN1s+3CSDGA0uqpGg/51P21F/Q7wHDgQDxRuTP1KQGNm4u8A7Kbdw5rGZUb0ZREWwGPfUpMU0qXi"; - sout << "TgY2XA4rESFQRvn3Uo8jPP7UOZgKM5bsdToC7Rp3wuKXJwBlmdTZgdly6CmELOILvHVNtGHg4yxO"; - sout << "XA9Co0RF7RsHOg1b9XzfeqibiK27ZiQnZ6sIvl+EdmsnxOY+Qa8oUZdrQ0JnFUiZjC0+P0760Si4"; - sout << "i36AOc4h+GzZ6WbG0yPZlFAFEcPiBbyOCp6VoY76H32BskEuSBJx0U5+iFKvkvK/6lyvr9WDTSX5"; - sout << "n74qoW6/CduCm96RcKbi2ywZ0Sk8vi6V+tWYUnrDaepg/iY84UA0qpAw/QsNZN99BqOlNEWftRVP"; - sout << "SZhPmpxzE2xg/SLUwR5y7NRijA8QalWWQf4G9TotViLV1IkftS1d9TvTMb38vGW4o55pWw5SZlzW"; - sout << "kFf9fbqlQ5ktTZVol/v/n2pwBjDeKu9wbNFlI7bhl2xhg/3nN2e3mW0HikB6K81JywLpxL+sa8+l"; - sout << "SW6bdGwbgj3BTAt9evqejNkQHVb8ALTkmZJUcPP3CmW0DWrKioqolNXrfVRjl9DLcxgQfhG7osjc"; - sout << "zE7/8gU0JYIk9Tr/XDHdk9zzOsMZAHu6nUbKpIL/5mytR193QuNE3x/eCQpsG1dMC/alF8EGn77V"; - sout << "QK8dhMFI3BQCXlo5TmLXd4RWOUmsNGt061t6ZQ/jvP7oPRskGSB6YUtr9Z6CN92wbnBUIf5VFJPD"; - sout << "qf0mPU0vKaMsbwQmD8+rNG/bYItW8lRAZLa4gJR5r6lULMKvcMiHeP9742lYL97T5w5VTo1c0Ec/"; - sout << "YTUMJKSvhfGPq4IfdtYmMOX9pmqd0VKlwnQ5w4pKiGRY3lj8zpXAHjraMA+46wTo9ZJ7Foq1XR1z"; - sout << "eCY3xmnahD5uHBR3Va66rw3W0WGRgbuCk63WTKDULa0dWuvNw0RWy+MkZzbn76QLX9dYGrWYUZWM"; - sout << "9mnQiC9yDObAz9vRcITf5PhEKduGFQgAl9CU0k/ZgmSmAYZ/wFJTb3U/roNqnptdm/KGuegjdQqT"; - sout << "5utMQZwwcgV8DCuE3/Y9DZcnEMoeSjZF5ugB4Hw30EJt5xAvCUpVRA1tygRwwWD5XiKt9srDjBrM"; - sout << "nmkQCbblTb/HTPqOvdkBO+4yHSGtpXfA3G0DdSFtFPO7OTwPLrUxf6rM6CQTgpzP6/eoupLM/I7z"; - sout << "tlfmNGcq+JYCy8csjKaNlPTJextxMVqNScbpeYwG/9SpjmhqObv+oYs/uga5UBWSyS5ls+eF/Tph"; - sout << "QSD9FkMCTY15Twa1LqEJy2TMZaDkE/UVJJB0Xe9eRQRXEgkM/7Olj2qsyN6VaLpzNBhcXT+Q7FDE"; - sout << "NyZ3C6b4MeoE7zRM0/KEXc25ma/uiHu142x4Ar2icGm7NYTp/gxVRUrSpqOOK2EYoHzCTFilsryB"; - sout << "JhzQqEIsDpNqmqyjy0BR5+rrfBJFxXAaEDryGMntT9XoLOF41ZMUAsypcv6yujSkbCemiaCTJ3gc"; - sout << "9p0IkAplKKgb0m1mJ3SrW2WR56ZGhZuVIQ0p24yHi1ECqcEK4GJq1jMpHpqIGfxVLUD8yZ6wFLDl"; - sout << "NP890I+aZC1kFSBSuj0Jee1VbQzTykN9oj6+8Bo8v9Yv/qt67WH1h/oIP5bxmCrjTzbncAT0zs5w"; - sout << "qzi+jN+AcQS4qh+IiuCke78wgaLtnMJIb1yA0GkDM6t9crv7vKAJwGkEFZSSg+p9TucFv2RqCCjQ"; - sout << "D5v0X3g0OpBql7+5IqXmuTiRCJE6BIBeeXVOtD0R6JORJ0e5Hy7SvT1LrIxISqZwat7//kb168WS"; - sout << "cdpRlCa5c0+ds81SVFkjTKViZAnFkOHPGVgzxrsdlfTt1T/2fx9MZO8HT3p5V9eM9XTASzXM/jMN"; - sout << "5X63GR7qz/5hpM12SMJvgcznXESiudVq7d7HYd0NeId04fzMlFaGtgGLDRr+15G0eWPCk7mcF0np"; - sout << "7XLz9ytRsWC/WXaVx2/ucmztaGzksA02If8fHmKuIHbfJ4YkjjKjVRrthMCOLsGYzfb9Hat6WbXv"; - sout << "jkdPQZd/EYGM/ZUd0wfuWo8W7yLidl+zMgMcJ+LeJjvRdBkHU3o70srnlKBaneUv7Ly/r2xPcsj6"; - sout << "wLMH9iuRZ7jZJ45EM3htNEHJzrT/9PxaGKiK3caGIilUnFPpyb+HgousVcBbn4XSdkIjy32UPdmQ"; - sout << "aG5w8GLvMEJ+SDAseg5/B2poqEiGstsA7EbJyJn3ptHk6I0lUFnpCgHI1H/vp7f5vhSIVkDyP4SD"; - sout << "GPPTMLfV3mmMzS/IeIdOLdshYQCrL9I/C1sgaASFLuh0fZoWnNmSxfS1dVA7fU597PnuEzGn4TlM"; - sout << "CVb8UdlIBfEmrFXegOnBEKu8RKspYa2TdawhfTZs1lyPJOxOewWH054k09q5m+p/QLHfmK4kr6wF"; - sout << "eShsc8VFczeBC2MtoBVF+a2gGsz+F2Q8WhtG35kJuTqZK+lqB8clFAVSljny5eVo3WJ83QHoZMO0"; - sout << "L8XspRqErV0+T/n7Uf9p72CQbw059rmyc2X1I1Mka7+cdqewt3RUZbnB7YD17+lg+do05keNDDea"; - sout << "kkjLqmK+oKKOJUyDULGM2syzbhMrJn3WY1v8RuEVsLIUrCxgvMzD/Hwcl+lC3idwjonUrQuWWDyK"; - sout << "dTjkimDohgLx3uoRs2ke/SCb7ERVtB0DMpLF3zytvY3D4ZGIdet4BGQ9H2nXNWMK9ahTfo4GFMxB"; - sout << "EBdLiQLAqBJteGuoJ1CWdYGj6BDbYoy9sXSdnY4Hw0a8ydeBmFqoRm5wZy6ozEpxX0IeEc3W0GfV"; - sout << "+4EojUBei+nfqmpeaDTWFgITbhplcgSU4snYS6dHxO4U66xa6N58YokpK0oSHkL42y9+Ap9qxiTk"; - sout << "eDRtTd1wgP4m4EZS6ld1rTROZMhwgwZoyKizgzW3cc6o3rS7wNBvrdRMtOPVo0YJD0UZJtwCyDzT"; - sout << "/LMXSVs5EQByRsZkKxNFMEky5F+D1IKlVny8ylC3oOI0VNJVxr+hIcuYjOf3adwKeslTQ2AO//HN"; - sout << "4WEyXck1inZFxnDybu7NGa/WhNdet+PzgzEI43go+Nn/9AYLjR3m7nAcBOGgtMqmrwOTPJl0Dhnh"; - sout << "QWv3JWK+OXqz3RFJ2q0lZP7A7EhU+3G5OHdHl6yFOh+Mb9TNh1ofYXPXL/mnnUhxCvUNYEXVdHGD"; - sout << "RALydLzf6oY5id6VdYgCOcQY6c32WcJUEk2zCeMFNxyZ1ntzmn2KlawbF6MPWHqemMEvJM7YCFXl"; - sout << "ObWzVlcxcnecWLltZB5xeRqDBcWetCMJvG5crQJk9V2jV3iXi56gFzOta7HzrEkKsjuYtys2c2aN"; - sout << "EMcOMedGcMH4q3xkzca56eoR5b0mXeZRnFRnK82LczMxJjitaFLOMTA84YAgWWpWt7ENpJ1qA7bu"; - sout << "QFdAdnQC+DKTJuLykS9eGpMZIuOarnlRH00AbK0j98XyjkaOhmL4ygSKNJQghgmFg2vyOfji/XNR"; - sout << "F2M6c1HdfQK3Im0qLTXaMZlEjkWPIZTcDtVt+CdUtiEZaqPQwW1H37Eqk9gZAs1TnLe5AOHAMWh3"; - sout << "mS458zVeG1d59r1BBc7pxrju9CWB1PjF19pxCUc71R8sh2D98ss2W02R8dqCAT2wdUuEC1WQwpL3"; - sout << "GGGEYk8jB/4ccjN/+EdYqAulxV/Bp+q1jspEm10/EU3Cpu6RkNFZj310v0t643E1MqzLjpg7ZSko"; - sout << "FkzcMRM2E7Z7WEzOQwbr4KVgXcXZ+76Xc1ahlvMqwkUQV+/PnYj68Ogv95pj6biD9vweY46XV2dF"; - sout << "uxp1FG2j2e/nzzFJgFfIcsB1411szIaEqmHTUPBo26qg/EyWg0775HTFSZwlw8iDOFG13OBlz7kQ"; - sout << "kTvrxy0iwvM6cTm7IqJUZi8hhC/eAX+Sdx8S6pKhOcy1q0fadSKF5cTynAQtrSYwDK0/xYRjVl28"; - sout << "4oTks7lHxGE3Jdu0+j2ufxNuem5fcEgWFSKZnZGM4XLvUnpVbdjPMEjf+KJ0w8LqDbTbmZYXW69q"; - sout << "Apl9sQ7AdAUcS0ZhGtACegAyg1QcJUjFvDKDhXwx5d1+0ZKChwYt8FFzqht6nWY9WjDT1i61XmbS"; - sout << "m2Emc68sgA9VMv1AL2bOhXq44//vlGYj0bX0gj7ZDEdUhCVNi5aXobYv5fn4nGfTNKp8njHyqGrZ"; - sout << "CT1xO+YrGMY/4qAGiBsOMGtbRrIcFHeeCMKOO/5so80js3nh2fw0L/XNVlY9RJXJA8WkEsalm5O8"; - sout << "MUDzmTukLJQu6YIjFhCHjT0FA9R2agaQnGFZXiiZ+0GnsRoUSrGC5mdkZ9H6jg7Odm+aB5fD7I5M"; - sout << "pHzEi++Jt8xHcbX0jyaQd4Y4pdku3xx1MrdgZht2WmCpUNxg66syuutjS6iWKQlvWSdDtlyf5zVU"; - sout << "rRn2ESMesAxpmuFd4sG0j8Qm/8jzAF0Fwolv5GmJFlwKx5X+RW5Mb3HZdJYg3xou0Uwb3FBgV6aK"; - sout << "mLA7GQUK92IMgRKKNXcDAPJdaF6wT4lin82Ss1QpWBBnWwLNTslobUbn1TJsg+MgurruWUB9byjb"; - sout << "56mNxeoTkMh9h9fEuDZYL7svjxh8U13AgnJCgchaDj7iK0TOd6b+sbz2DadE2SoyFYo4XDTbXcpE"; - sout << "LS8TbCb1rH7G2WAS2a4JKRyahhELZhOB/X+jPRtre+MNSMvoYOFYtHYXjIzoEpOgEGdSPOsLcA8s"; - sout << "JHCoPcU7bFQcnIlB/HZVqbDZwVzAtUxKoven135MSyoRVu8j8nTIcM0RBT1VjQa0L7eHcgQVHPvR"; - sout << "uhtFmhtUJJT6lDJmoolIJt+nxeT45+ndSTz16YrT4pODPMuawO3XIULSvkPDWNhQybkF+2jmUqlX"; - sout << "brWKyviTX/IZsHS/0YMkgXZRX5McLzUH3Vs1PcjNBoj2q6tdCq2HCcIdBKH0Mokm9DLqEi0oY4mq"; - sout << "kEsB6HFUnYZm4xezpAoY/wHkXRq9Y/6lOa4DtgBz8ng6/WwFwKwCIkT3/aTHzKDQSewYiU5jmQGe"; - sout << "R4+wtbbRqGm4yfRc7z0xEsMESF2FJnODPSSSBAFT60rKGdGK4ai0IaYbWoWaHeDxhHVeOUYbwUlr"; - sout << "dBHstgs0/k6t3QKbHDo0KwMv6LN0sYYDK0fD5cLA+92Pf8mOlOAS2YNlR0sG2GrSa8M2gZhpgTcf"; - sout << "dz4j66DDlLmQaC9UozZNf2PxDFLUN8NEHu2XnfzYEyuyt0BQcTQyqs8DvrayzIwV/qiD4o/xRI0T"; - sout << "Ma5RvRpagaW5EUApHgu09rkvFXk6ijL+2L+7j+lDQTgkvn+wHYwbTADm/b9tZs2egY+6pSDDnK97"; - sout << "invYOsWUNUDtgvRxgFTDDrjUNX4xNifDUnGCJrODnhg41dOG4M6ST3gBgxtKkrmuHkcgafuohkCm"; - sout << "w8bPHam0dnd55H+lj0J74uyhw/LreRAthXdcR3RbizO0rWIX5EDP5Y+JYxQfejANE9APE2zFHg6f"; - sout << "6y7DE+GVQHO9iN/N85TOUhN5ya9MZ9fk8M2JR/440Q7gqoqNJ7w1PpzfHCyQBSszPQTPBr5WeirK"; - sout << "3D9axZGpND29nMOQ8iZy/7b+5IinED5SDZq8MTekt6kgW/ScvByBVOeBbxh38DRAcClRnn7CdFT3"; - sout << "Ey+kaJAYlahuErS/gjU3VkqPKGds02dkPwumMWd1G6s37r4hQdPoO0F73+5dxWwqF1dX75nQ5tyz"; - sout << "J6f2pbTFnJcp4+CGMYs/SEoPzODOqMbMt5hDqQHZuEekNwMNI/X80PhnIOiMa7oK7h+8cOb9v4Fc"; - sout << "07AR19kD1T6r53s77u7o8wYLp7/uf7NLT7tA+bc6Qeaiss+JLJznbb0LHa9uSUZ7dK0hvHFAYBjh"; - sout << "fGqkQ2JI+adCJ/eUQLs9zvo/0GvEV4P3dZlRcuXw+y0uj/HhbMnTYgnD8AUbRYLCLS5Lx00m+Vrg"; - sout << "lVyzOLy3d84zSmZMR/xoX1Q27biAyEnvZK5vkX9CmMY4lhP3XnEBfcK/UVxxZDQf5HfZIamjAbOP"; - sout << "ZRyNX/+VYpWja1qGHZmW8Tc3aJfH9GsIKdls9/FKfuydYwfwr2kPuG+PTwXxiHKWWdzUXluLrmB5"; - sout << "T9UCUmf1mkBM+Jv5r5R7qUPldDHzJP3njQfT4SUrNNXL/ooR2NTX/jYMqkTlwSCM5w7jYa19IDe5"; - sout << "lYzspMfRfAnjG/DPX7m8H3fYqHXo6JKfWGL9NsuFzFPzbeVqVac5AQ7XcoEDXj8b5OmGu915yRqO"; - sout << "zs+Oag12V6PRX1w6EKKyhQT9BPvW9E8I9kSKGZrO6rSq9pBaZk5DWES/UX0U7yCSHaBAlYZP2fmt"; - sout << "tvEMLhzKwu5vjk35yr3Nm2ICYBow40OjKI6ft7EpneSgGbkBuHH7X2RxfCVcg0+7THSBMEMgnBzC"; - sout << "+8JERFD94nZDWrX7P/QTaejq4L8yWMb7uIChSHDnIIUleV3h5kGuM0MCRChSUPvXUESKuhkezPTF"; - sout << "KRt5t3r1CbOHFwnuIjB5JAMUhCUF0IEJtk1b0gnCY109aCl/sefPp3yznMH8tDAWTL6G/aJbRLxv"; - sout << "DpBb20+ZyK78kfTrFaJ6xGgAyi/fkcggLa8ANOwU+ZbVKvVEQOzU9d/8ygn1SjNBASKaHfVja6Ym"; - sout << "TffBgjcGFtnz2NkjUBccd83WqOyZOxsFqOugfmoavukdhDAaVtcB/KijSoBYQVhNITRC7/RLRJSE"; - sout << "/obYwojRxTfq//cIE33pXW5RCDP0odyGIr4YVCHioG+li9NK2/xrduroOiLdaKly5eAaFZgiFof2"; - sout << "jVYA3mXRxwH83YBdKUSwHDhItv7R2lAT0nsfXopxcJBQVNwPvCabAmsCf71FTl+Wn/LfALLRgtvl"; - sout << "b9JBLI/gPf7tKXx/MhCyz0Lq33VoyOGm246DvalV2QChpolNafxuxK5AnvLxYox3U+53A88SddqM"; - sout << "P+k7oftTdX+u7RhDmZtjHLYn4ikjLwK+afDTW4hCBGFR/bmDsZvAyp6OOVnnvsj9qSOWpeI0VGQe"; - sout << "tCaSgoPEwP8GtY4AX+Dhy1uIYPmoXZUPyNQ+ng7MlCk5S6HAzLlsd5giVhyzN4b4N7f6vseMubhI"; - sout << "/lG0Hfcg+rMX2VvTF54FMPgPjkf6CWr7QorPDB/Puw8c3dt69dr09Q/r16t6aJmtyVa0EzAxiqsR"; - sout << "n1POTejlH9K3Ul98w2mthFuaFVwk0ILJjCZZVgY1GxlvYRzPs1/qN+VQ6GdLYFaaBWN3mI20aPND"; - sout << "wcKX7LEodSgu63Da0BBrKBkoihTYg+IbO9m473LWf9U4dZDQmSjJeISGLa7HDoh/JAnfp0Ej3EVG"; - sout << "K9oe/ewohkPFTgXIV/qg4LDRny+zIJ+jsEOHeryCzBfml0mR+WWwwYYcPur4cCABhkZfKGVH5L9o"; - sout << "S2pTxevQeY8hDj9dUblfU7W9RBl9t0j6pYm3wP9wwP1yh5AKezedXy5VavBPk82SzWBqtsyTpODJ"; - sout << "AWQzgF5G6yLvPeUChHRDGedP6KzrPxkSxpyKFZq6dTNl8g+yLg/FHwOZH/R8GesbKKCxjjeZqD+4"; - sout << "UjB68IayH6ttcLEjncK/rLOWn8lWrnTsQdOf7GznjWDHILJ9B5hxEsKMKqOfYLKosPMpIJFk8UE6"; - sout << "CYffnCZFI5/M4stj/2A4zYt2kcqIMN5rIBJ0hJtBlfZRTMweBmJjhAKbM3b/ER6jQOlbPBzNNEmN"; - sout << "DvcgFIL1c9/mqfI041LVldLTpHeaNDRVVXzho5+iVi/R5UfYYTvvDU48E6cKh/ErrdGHXURNYRXZ"; - sout << "ghNAKIOA3YeXZk0SpcdzxRhQaFwLcim6U1j3kVLEYnk17GavItFxjv+22zr382ivLNfjVdx9vxBj"; - sout << "bRqj1fy/goe9ilHC5sxCphuoe9vfAe4Le0b2hqn0Y3LyO/lvzJEVbP2hkFbU06YXUOktXJVCH5bk"; - sout << "JO928XrFA98vOciWwqtRXhO26A5yELcjimSmqZ9zwt+TKz5cG53+CXufCssHPHDSaZCbF2gxST9/"; - sout << "TooDJUbagLQ7VfNZMBIwDVoLpl1dG1Z7GgobQ70lkUAA6Rlo7JfhNM5d8N8UgM2dPPukCAQ5yuMl"; - sout << "0KTHpW9a+qp3B3VE3+8mPoX16p+zpRlw5Y+jA5ctu84Pez0FqZcTmM4lFcevPe6C9LCxJGzKJJer"; - sout << "yGyryVbEzPmVk6nsuWnHFodPve6NeeYnlTkifu3FBfP10DzXYSUO6nPwSZiGiG47AYnyQXmIcj0N"; - sout << "iSwKDzHcPf5IVgT4qZswNoy+t8QBATl4run7klhzNdE1QSAp8eNZnjtdmZdGY4UhB3/mto70MIPV"; - sout << "OBRoyQCHkzWx8tPcn7NMUW9lDJdFFgNiHngYBe47fz+Tkuv1iBxRppU32ByeXHOSKMgaTyb5f1XS"; - sout << "ern+rWMqDivMybInX6vMV8BfQ8eOcnl9MS2OblGPj7XDsbLmr/eOe4cFoKdBKg791eavES07Kr6/"; - sout << "LXw/mELHEEXBqkuafGTUlUfatUz5OmHHM6ssKoQM1uUrGqzgcdiAe8vNxuMsapJYJjIb80meaYia"; - sout << "EyAvl8dEYiGSx5etaf8hwCnXAOzEfHNi+qjIC6Dqr5s87SQpYPDexkuUv1hEGSDnEbWuDo7BSwu5"; - sout << "hZCA4T6yoSSki1hhXMMsl0tR45bm6UlB74VX1IsL7VD6HiPY3BcveCMbk9aNCLt61KYwY+4+xEWk"; - sout << "smbgq1BOC31buf7xQxX9UtsyBHQig3dCScPoFBm0nKaEHK1fuf8zN2WIi7+p3pEQqRTphOxHtsVk"; - sout << "QyC7PmZJS4merPZpki9Mh/jo5MM76vCXgESUr91CY5neMliK6mlVDQjWBPenKw6JwM2UeDjjQHTa"; - sout << "5w1vSg47kgvLT4KUfqQjS/JDx+u/RIrKGZbsj7WnZlJXsBvoFYn5N2UGBj/SOLVmCXYhBfUHZxqH"; - sout << "GvrtVWHffKoSHcVlm0G0u0EKYYvrYCydASHOegavtcc66k4YIqge/pv8j5lTBiswafWuBz5OwbDe"; - sout << "zxzAcApflzsfgNLol/DNUutk3QpBTk2qW2XXt4r6tkfCG89NsMcr1L3E0+NqWc1IbIIxJuSBhq9F"; - sout << "wNvPOBUYmMD08jguGFHHFLFTVAhcxP90IP4NN6/ImZWqUmZ7DsQz27ritt7RAgjR0aXUJ2AipCss"; - sout << "LWr6mIptUwcOwPHwqwrb+s5xuLIGsT+hXzwpaNrOq7AjKx4GbRcL2UhNwDJ3jAQe8K6uJxffzABu"; - sout << "x1FLhLe7N63S+rKYHMLSSQMKCdf8uxVRz2NX44QRIdxXpV5/mHXgDd8qysZkv26IGp7DG6Pggag0"; - sout << "DtuCocsV49swsov9/gw2yR4r8ziyO0oUMOygSA0Uirda3B1MOiYAvCULRc/HyzndANKNjef+uagl"; - sout << "U9r+o2c2pqApAZY9JeKNj0vbW5mmIZPU9B+/LNGnLAmJvpbrcRTpkHI/0VUV541n26da7jXoJ9y9"; - sout << "ejOkZUY9xMjkLl4aqgwi4cfg3TJNC+0GXjM7aRlJPk2uPNujEiLSkrPk5GX1+jlZIdBmWSfIkNeo"; - sout << "XVQf2dVu1YlWKqmRHsgnJecopec6lhGHVIeamgsyyO9GEmQBG0cZ0lkkq3Fc3Mhakcbnw8InWFYe"; - sout << "skGjFM2kSHQ8ImGUuGx9UW/cKMZbq++oZJQ8y/rmTDfzASJWXezdCOJsOAEAqstGrVZXO0Aoq2Qr"; - sout << "rwO6oJYlxdCpc4MLBzriX0qlA/kj+3qc/lAb7svIugXusyTAshRsSeKgBb4qNUdJ+31j5UoZbwT1"; - sout << "LQt6WHxVFoqYzdjnYsdbnSbV6llixlEfdmdg+gyAQuYciR5z4B4D/dydNjmbFU2BT9hbeMqkam/3"; - sout << "vZSjziXOOsqMnGj5ZA9yIPwKcU9IqvlhxSsLPyFN6eTq7EUV6/njn97pMQhew7hrhXWpoq66A6lG"; - sout << "5kE6M2y39eZuQnh1GmWhF2Pr+0Y0hk2WHx+h/6sRNVTH2qOm0wFh3g2ZqCPgQSFSdam5f+Jo5yYi"; - sout << "IFxYEsif6HxxKGg100Yt59WpeKCVjYO26uc+bclSKZ9VNFsowfyCI3ZtFOp5aqZne3aB7tNkqUh0"; - sout << "387ffCRsqMoQTLLZcycLiIpcCgmOCb8ASCMi5WgPrtZxKA9oB44xUgosSQlRgchUdu5qtROn1Q7f"; - sout << "PHqeNv9EyJWiwHUyLp26f8pus1iHAYIJWvoFj09Q0vXiyvPM4iSd4tFX7kIXxOSNy3ch1FFHWc6j"; - sout << "FDiXSs9Lf1iWpvF0O9iqXZcMdez9dx3lnNuXBttsbNFg6vBABk8K3DFtwDVWg6a4CCFlVmucQD4F"; - sout << "Nf2eNdlVUwwpJn//rtDlvPnZXqpqx8JCEyBcYt0/oYT5vJdrhOKRNiteeUuWf51JxdJh4XKnLE+e"; - sout << "8JAmuTL3AJ/gp6Bxpi+4P3jAR9mZfA/sQnTqasw32kz427/v+hGWMq5j+Ak0wGrKdTlDeA7KLBGl"; - sout << "vbZUrSR60wvpL7SJSYkM5XraJzR5ICO+IxE+hfsDR2uTSrzfcsb0M6wnVatnXjOFNLL5AY6jpHc5"; - sout << "sHLhUHSziNpnsMsWJ4h22wbZmrbO0zeD6wTHvUtrTwOTiPfGkgvz3DLcNBzcn4oqfaEk5JnXoE06"; - sout << "eH4+bqe1/kZ7tlZLzjd+7lFAO8dlAA1KoIGOvAL1L5aqnPbCuJVaak81GSiwHlaztibjOB9Qolm7"; - sout << "y/ln+qnZ5nbMYuWnn3lN4m3Gnk692nuMfiqh4RQuwwxzR1FZtiCzGBPkVD50VMzUsxs5d170C4MH"; - sout << "ER71dvl0U9lLf8WKsHyiU79tJv4wTOpzSMszbf+WCpWZXCJrfyJ6ylRO3dvk1QfjPmFkUwiuQy67"; - sout << "q+lFfRo9N50ZvVB2bYNsne/tlNHPk9/WdeP1rtTyMBMdl+RfJUf27wduC7RQzMtdUuhFLYxdDGxm"; - sout << "1ZZBolFeteOW4IboI2KanbMlv+GOAwxkAWC9ymADZ9YBecvz1unjZkLSGzge1MY7ORdnLBA8fyUv"; - sout << "JsigTjg7KuX2kpkuKxQHYs2l78Tlkdwo3x2VDKEfNegyiZMGMr8CSksLfxnI9dHwbUmyBQWMZBOR"; - sout << "gWU+s45M6S9+HP3R+lwsT0Pn12HpljhnPde8ZpemkMsTbJW0rCQnYsrdryfL/X1GUxUxhNm5WJk4"; - sout << "blqylbW9X7IbbUCB3+8A0phl522/wLWusDD9ndRsiiXj2EDO2Ah5MXodILg7FBo8PpHDsgKBLZeK"; - sout << "WlYVQlWNtg6m/jC5004csHtx2ra6MjSxAhxyt2Y5qf90KBDt/61iJfKC0N9hWm5KKMsGntwWZBVe"; - sout << "h8eJjOzM8vP4azMfymC9U87PqPuI2/J+IB+JNVnjLsx2z2NumqgAKZc4mVUs0PiNK2sSVRlMU4uF"; - sout << "DdbEn+oK+iQ7ILBnkRhDu/C4h1eSyAQwF8XENwyYaclxhoVGRTCRkqrOaLh3SSb+YFSFvbDz49jo"; - sout << "ZcfxsSvLNgIeW0MnmYUH9ydCunC+y9aiO2sZTfKrl95BeX8aEjtxCjpqVj4daFz0diNEQSiWIWlU"; - sout << "/EqYRDfOIZoA5QkPBp2rioNMsElAHaosb90vh+LiDXpOj5cz0S9cKvQw7hI5Ma0gVmhQbAqFrnOD"; - sout << "Me/94YZgsabKZ5IxOJrajCoF1WO6GgdJCkdSUrfRZTkxe0y4qBPAbVg46BGOTjnOkSxUUzXAjXDE"; - sout << "wPf86SdrIWq0NDJ3N+D2lu0MloB/tabrVHmBXQwNDqk24uMVQPBQRqYUGCTzkjr6awEz6oICB2iC"; - sout << "14KqcsiwuSxIsY2jyL0VG32aHA/XNQTZetsvMq5fee0jzSZWAqA0EFzw6UFW/kUmVEsBdX5kpYpS"; - sout << "XuNVhtxVGey4zANa9P7nc40VmxDM++pP/45XNbpyIUOPxfcBM5YoHjnpAwZlTTUsBi4Kdx2D9oRx"; - sout << "PGlK+TGQF9T9ZvI0mxDTXiotRScPozQ8jom/oINYIVzdH7EWLEG+nmKib+3icO9A09pOYWw8TSZj"; - sout << "HNAxPczCwc98w5FdlLe4zDwHnILl4NwfnAz8x0QU+VyW6kugntyk+G1NvFHIGvOnFnZA/Ku1nqM6"; - sout << "3jErxhCl0Ii8h4dD7+HVUu7FWUMOM91LQHsgmphdHA+NQXm+/J/zj2WxamzK8pfpnq9SCYpNfjGe"; - sout << "NOJGOuytsDuG/Ct5fik3DquGqccuhve3sw9H5RDhaFqjWOEPT2rhlu66UfRsyy6Y2FwRWcB5YuLZ"; - sout << "+96O5+VUhbSynvOuygLQfmCZb4RR7h1x4RLgimV+8DdDaH8abjuhKv9bFlSFUNpireUlxkYk1NoK"; - sout << "q+3XpmpJyDI4N/qB35pf7RUdp0mlTU8/cF7g4OJ05ryskis2/tURAdoKcePaWSzn7xoA1tCGYC1y"; - sout << "+Z9m/7erwcD8n+1ZHu3499wbywGzscGo0q7Drc4SxiwUUDSH2FWpaoUmKEUjsYLMwx57QPZVIiH8"; - sout << "WjLd9hcjaht7OqgIQI7ihdSY5TTvihLif9siWmrJk2kpl7Qpx9B2VSVfFBrie6JU+6Ecn4XIMY4n"; - sout << "GYVF/bao4WKkobcbSYCmUfTr1gx7hh8VkIpmuPfiyBVHXI1dQtY/BDxkLIBiUIk509Kb/U0xA45N"; - sout << "+NPG833fYqJ6p9ygAJCQJfmymr3TXL/TxfXtSQrO42/upQob6+KEjSBpewaULLu3+6diY1/P9n79"; - sout << "gLnJOnGncD988L0KCthNKXM9Rm9hzGjnObcxg2IHckRu8F5Bn298963sUe8x5G5BKCLWTTPFbBxO"; - sout << "Mr/zSVZjlKaafRQYBWKE3BPQRbLKBYN+XaNuEQ/cfxnsN+gUimbvpZUueMU0Jeo1ReHqZpZnjvAE"; - sout << "Upf5Lrmjpa0Iyy3irUQ8HEVEisLMdpg1Qo5RwuWanqTVmGxW0sCjfrCOhV3TwIQqxeWPfc8o4cRO"; - sout << "uN7IkWSyck2HR/4ncq+tfaFXehMA9rxigFkeXNh3divWzV3W+U/SeAAQcrgUgecpcxqmGkQjqV3k"; - sout << "Lttwa2MF5SCMfM8LwEG3Dl5JPvPfTY15dIUGNjEIFJ25UuJ1vK9K/ns4ifG5QxNKGFXLvgdv2WXy"; - sout << "0968qdLYeIMXJAv3ieTFm2ivd6OVs7c+jp7k2Ondx5e/WeC8ciCOt7JKyAbJcCZYbasFlaOOeNOO"; - sout << "tm7/SmLUp1hZALfjTeoDc26EtdeNf2x0HzFsGivu+/qd+eYiQXLGqStyYPVc2IZ08AYLFClR2r+L"; - sout << "to5+RouZ3CCXiBqoaoZVsGiqyM9Dgt1E3PlaXtgLFzhpXNw/l4FXFKPiqS9DZh0X1aNQE1KAqLgM"; - sout << "F/VYjhOpbzycTrRhiJ0vhzaQUiCCe/8QUlSqCVVNZ9w6Dvc7uuNNp+exxN+IjPcWoSYDe2QW4MHi"; - sout << "3Sk99Bhu+5H4tVqc3ehExt01lIjQI/M1A1CpuwGv9Sz3/tspG0hYpOT+Mo7sHZ7ojYfkfLHOfp9J"; - sout << "gtUT2uqV0LtDgctrD5vx6NEgfMzP2dNnj7wopH9P5YsTv2XPW+bKxUp95K5KMz0Ea2xLmrZhZqG9"; - sout << "HXhubQS90G74ibC9p5nvDts1DoIC5swJpo6ZzVude5M6GmzjVUHTaS7LSjIbxQBcjyFrx+panbfB"; - sout << "TLLL2VaFjfxw82RGjjQtaeCeZt0BsnkfL1A4Vb5ItP+qhayO+9lqnSXdECHa1qFTXKDVPvD+SQC1"; - sout << "cXQnxXvCxuLmHf/ao8i5QFXd7tHr96ioCtng0PWHxbXyr3NQhriX2zbwD2kkrrL4udvZo2TRIe3D"; - sout << "/L46KWG8S+zpo/iXc68pt4sjfuc6DNIXDtx8xkFAre/q9dRKmuV1BrtlYiIBQlhDByyQuqD4Cdwj"; - sout << "1QMd3JkiF/WXCc3vsuLH5v54EMHM02MkS0TjzLjOinkMT9s8u+ssSyp0NUE83XRdUoND1Kw8DbI7"; - sout << "e8izxcz3VIpR0PE6MYUKS6SR1Gq+haGlgnZYxBCbrhSkTRn0VQ02rnBF9An6rmXRHbqs1rJZ4yXX"; - sout << "9/jj2dPwYrFW5NMXL35Za8fTfG6pXaDjPnqJokXt2rKX8X2/DVWhYQwBLpZytXTc9RpDis4VA6ao"; - sout << "0ZG/u1qgQeqsmhkwTayh6S+VFpwrQA5upwIL9KI6JdbCCsQhazF7qsEDoc1GeAntjyqntc+mml41"; - sout << "I1ZCYtP0sSOWfWiQ5yZavEsrp94P01thtx7s2UsrMtrYQc5hxs/J8VWtIIpg9AIqDW7g50qsBv4w"; - sout << "id8XQPCcfyXQszUJi+w4Kubu+MPwAMiAVkDmlJGAC9t5oXIIwIbvNZHrq3C1ixd84+VqhvZZgw+/"; - sout << "izIXi9IrhvkcOJmuwxQ9FCyd726/ks2e4H7TafuTXyE1RQ+6Ju3odB4meFU8SIylbCw2jwoI3kTX"; - sout << "XXqHbAcdgMWoxoDXPZ0XzVqC3utfaOARHji4+RXEseqA3tJhS5CPHj+D+6yXJivrVC6KWSwPqLyW"; - sout << "6JI3rVeT9B94EYtGH6qWvcA2GaSJjxiQUZQyqahHk/FxSkmnMTPwtvV6AcmYZjQESUWYI9UQyHvy"; - sout << "5e/ULXf2bVF3dmTWoKOsDchTNOQ/Vtswf5avr9tuz841NKT+1fojuuAhhYP7uvRruvMJqebeiMIL"; - sout << "QBzv27ji9+tQ6D42+Z5U40UW5XtytFKcGR1NWtaxNkWCPMGRLcC4mFLuQMYq8cK3ZZDhpzyVeyIi"; - sout << "nLJRFigzQfHlBeJmXm8UccbJYo6oruyFUEagjILnb+fb4y+uE7vN6WoqPcg4vvr4RzSkuHueZxHL"; - sout << "5ks+O6XyEywn5hU3kqOmI21d+C/HBRjJHbiL39BcHKdzhnHJ2huEtwqSV6SsDiqhxSUL7zYEDECj"; - sout << "zs+9/3LHJ/9TbZZ104stYMWXIWuLc2NSS0bF5NHarE4XedBAcXaJzKzlj2o6NYb4Ifxcs0shAxl4"; - sout << "6ptwCMOtvcTz1t5QB1vG5CHrc7emzriP7+H1tukmCRA2i7cZPumoiw81AM7SPv/A/9yr5EFRdBmv"; - sout << "FCOwJNDKfD9Mfb6U1Lk+ikWFR1wm3MSqq4ZSy1Q1s4F0npfVh1J5nWZTxd0Z/TqDhSAUU47cC9do"; - sout << "qRMBg/bBPxfrWGeDEOLActWvxMr2g6O4tdWLxTtWcGsGiatS9TahYclwL40gnzAC+4g1yOgc4/u4"; - sout << "Ye0T1KyUWUnAfpWmFibwphrKOrVj4ZBVzahAWMG4jWzDcxTaKoZvAtQZi2FOn4V/+bKMURZChDl8"; - sout << "DZpqrBSfSyY7DnPiGRgxOmTKK6/kb36nlas7zwcQvSgQTw3vPJSU+LJg4gKmDycFpl5deILsDRBp"; - sout << "78NSCvjJqK7UVNfdAG/2C9nQHNk9nHEf3jo4Er7ogKGkWOHshaIvzL98Nz8BHbr115gtKo06mD+C"; - sout << "0EJGtywTGXAzkO8J3T/gEuW35KH+kOIBMXzQcJNY1lNud+fALQxNv2Rw5u17KBXqLtABWVaOABgz"; - sout << "IVCkpM90yG5CwxkzyKO+CIxb8JyNs6Shk+CPNoiU8mbqMFigPgXw+yWEEdOMvTdzJuj5dKX28e+I"; - sout << "jwOz6wcXPYod1yzF0kPUlol0XaDR1m3abfEiTwquKsTY7upWPjU+Q4yUnrP0/+W4Oe6TgH7JvEvy"; - sout << "fhqLicE67NkotZ1/+AMZVDYWZOScrGmi8c9Yj5dg3lWi5OtfLYg74TSQ8AOiH6jL+wLc9J+QwOaL"; - sout << "gibI6x/dWW6eJGHxInVjQrt0xmP6I90cDPOnx8JC9tMAEv1MrLMt9VKpjdMut/Gok2doLdEHyTKL"; - sout << "wBNiLGq0viiPXgm9/sbi3KGrWhOdypAbECm0emMVQK/WdkSFZu6PSArdOTvHthXOvyTb58fm7wS6"; - sout << "oWvK8J4f/T9t9aNb4lBAYFtufB5S6ndUr0hEU0nirxgfsXl9ZsRpsQidq58dK6l6WI5CmUpYboL2"; - sout << "6T66Tndz5jnvtPAybpIus/Kbmh3tPsaVtI7e6IRl5mzbEngtlOqfbwlyJyUiC5UuGJAUCtqoKadJ"; - sout << "QKGtTuLv2VqavDmNIuZ1mRX7g+2Kbf6y+7L42iqivvX0Lmo8YSJFTuNkWTNlu7s+yYH91uC8BdT6"; - sout << "3DqKRgiRWVfgKuuJvDp72gyJknKesDa36+iCp2qaZZ+yU0XhK/6U2phZ6XeNJlQY5H8JD8pe+RJk"; - sout << "qgi9XqP8qvLIIv0RkfnDVJtl4V8pi6I6UiaD3dNUeHdN1YHCu+Ub225CFQIDsko0Ly87Iv4oULoI"; - sout << "1gmb6dLkogavqkQfz69GYinC/z5/En7ekQ2TnOgFiPV3SaF70p7RmyMnn1/ON9r8vd4NfFPXXCCL"; - sout << "NBSihPK2Tt3T5i+TRXipLYq4+kghSyTRhT2YRIbpvT3yYs1EGbEekCQQmIltN78NpyWqBwBks9Gt"; - sout << "xpIS1Mj7Ff20DpfjgaSok+XhDSDA8pjNzuM4Hv55IJbF5se3Nj27glEhioHCvKzx84FUuInAjQtT"; - sout << "0+5hsgLkTppvJTdSUg1hzhU7wa6NixwuZgfUenls7CwH9K5WYB7KN6ls8gzbK0V3QMoyZBRIoSuO"; - sout << "7yyzgY5erfXbwfcYQ2LliJQQyYyLiEcm9HCxJGMuBL/41LQxxm5KBXMZhJx4c/S/54GJmTMDfU+3"; - sout << "xceNujbOdtlz9ahcxUBPGOU5iTYBGVUPFscRy0nxuHhOu2VI9rYBhoIUZs01Sh3BDgksu4uUnGof"; - sout << "crDdCqjpR+1UVnpoEmxbW/Z1Ux03tiG90lCn58/g872xPsO/O/DDX4wfNU1yeibFvWrW57k0FND/"; - sout << "lNNeLErDW/nBpQSbaWal3O8lfmjD2SBwk3xtJ2qXx538rFZRM8DQJUOfUjvhycRGITPP4GZBuiE2"; - sout << "cVpeof6Vqbe6J4I3E9Bcrnm3moAyKGiPDJ6UqthQuILGNTH2GOOXAl2I1X0qcJ4G2H5xgLNliett"; - sout << "T+uB6q/kG7Ab+4QNVm0j0zdPdVZdUUtGceMJViJ+ZoRVjunK3QuzwgBHYr89OqNxvULc7tImHva3"; - sout << "xan2frwQ2ZzJy8hRCIdaNSNgdZp4WONm+5Bw8/MU0l3PjjXjtlzjUE+rHSwdv5abDy81XQwkDP0y"; - sout << "CS2C7MMahBsySi7v+PrLljFQbyG+deR6ch15UfFjtyd6Mctr8FcW2umvPYSnl1g2Dl0jE50ZLOHj"; - sout << "2gRAl1EvFXIuPjxcA7gsrlATb2SgCUtrelJSDOJpf8a3WMKrLRYp2MONLXacFRpkI0iU4fNhbzgB"; - sout << "o/CdP3O/AMGGnaZN5kIkmOwm+naBEF3mv1xFE3ZZ5VldoFrWrxZ+9s4lOAScPYzVpABjbJxzOzSo"; - sout << "fz7+pEOY7/OY4oNgqWA4MEtO2pMbjUpdTbSiVuXD87KHJYB48ffxkA5aPKFWpdRFCJHM+J2Zjkaq"; - sout << "ZRRUeBZVzo2rRvFBWrpbvAkkaXEDk+k+N3mOn1LF78Qbm5FWAn8aw8QL7oQnmrkEqUj9EAiYTx+q"; - sout << "v3oRQ/x10A2LctzTyy0mCuM4bR+WQW2h8dGoMUhjJpGPQzeR6owMDi1n09T84aVUDkxfR2PpIHxs"; - sout << "HBag5ryae61mzdzqNmyFzySbIbD83r7ML6FqMCW4xpWF6ru2DvNykb9GYL/kquN1rjNOe9XYEQM8"; - sout << "pyYvcipUqJdEeYj9sjgJBXkuDr8fCBpmvJuklMOeuAwAMXwxL1b53VM0XNgVokPmFTSF5zqesA/p"; - sout << "ah/7f2URRjrP9RR344YUdzGoccdhpNnFQ+g4KRdmd7FuPTJYePyDncJFnVZNT1TP026QrRPzg7H8"; - sout << "sFMoZQuzZxjLy8gSU3JYcBCnfqBdcvrtF5Wb+pKkEZxROE4qURll4Y7zYVxPv03Z2PvA6eovQyap"; - sout << "Pr2jTJPFgyEiAtcdsluxtc7NvY+B0Oxgk+oUyaXAhkihKKSGT2pPRUvd0LqDYU29/7v148UhItLJ"; - sout << "minqiK/TinqUPHOe8oyljHRIO6awrl1oc7HbFs/8ZrK8hd07LG27lMDcrBogEyUZ+mcBRykohy5w"; - sout << "vEPLCUA/hLP4NHaUts+q33Wtrq5n0m+4k79SRjwuvmcSXjf+Uf09CQD32oicQr0UEfo8uhrUPwta"; - sout << "WukfxXE/VF/jfynSXwEomRZopcNoc20gWzRlk+M7HpjY1X3TCi0T6f8qAJuTjSSbUZTFC926jhsk"; - sout << "a7Z67jTJ0woPzPFjK41KLWQcoArKTuqOytbd5fKao7qpxiFnUPd30dq6Xhpnt8oW2QESiOPq/4mT"; - sout << "Bdtd+wl8c3bzgxRpOGBKakq0iI5Vc30wc3vTUw8Mpae/beCHolCN52hJF1LAKRJT4xYWMHLpKwNl"; - sout << "ZqpKMybMAGM7O5X3xqcE4nH+bPkpkMILmNN2x4JfWYfy+667pQdIed0jEaDRHueSfyuXCp+Yu7sV"; - sout << "q7CTe3bwrUSklM7zpU6NxuX++oMNuruKzZ7GwsP/NkepKp8MGUyoT8EOz7yIq7k9z61OLxz0Ylxr"; - sout << "0irk3K8oCPttZz8y3u4ZmkAGcYOs41JR3Lu7D+/V0hjdGtNdb3ALJON1AB7r+OQ2LW3SEP87POnT"; - sout << "R3wayKImuVIpIjZi+yBSMqomZMtH3nExOSKTHzLzkSyefGR3WTSIXI5GqtWNetBTk9y2eeRzbSu5"; - sout << "+7c0XsuaWRQd+2qfns49HNwQocfbM5kNMKa8npRCg8YUBMimQiihwEgM6NJQWJ3ig6vwXncIOAYI"; - sout << "kwcYWkLBme6vSiaeyLRG8y6A78zeoc93lGy87yjRWYkr9CeARb8B3tHX4E1APF0Dcs3/6MTZhnmv"; - sout << "eMdEj9TW/MFATPy0BELuV7nLBpSrH2Intvo1jzPU7dGsw/36MbHr2LFO9a2PrZkMBCixQ5kApER9"; - sout << "HQCKGangWrdU/jO76/8j+mcrxFNUEc7y0OUUFK8ZXwHMXQp7MOew6OVUmRuFSl/jv22Zdz16njaZ"; - sout << "Vd7fVrgOdTG+EXRseg0LvvzZ40qWpRahwg7zqP/sAWp1ZhH1yJVoalrCFuTLgabjWGo5Amp4vyk4"; - sout << "zzseJ+O10oZZrWsFOg1lbPjs4j+U1IhQ48qNS/zk4Y2kaiM7/0q7PkaUxdwdcdHwbOmp7zszAyfu"; - sout << "rwXlt1PBiRyEsBKKKsBLwUzP/KJEoRvFvgc2VgzDxxe7pOsX3E6+ey7gKt08W7RNTTGx70YF2uVG"; - sout << "4cEQHQNqLygscv4DllKnH7Jz+2cHnSES9us1PaWqRwpQ07XG/sSI6t9o8P0xMBVkR6XB9JkdKP1o"; - sout << "Bj1MgxVN8pMbv3ANGQzTbmCQ5IczVK1Io/OQbSEVlZb8ZOatdQ9nq2YZvedMelM3C+t00b5AaQa6"; - sout << "DSslaJ3FWzJLguvwYQgYUKvJ+jOC8dMr74YNAxt06b5RaijSib2TNDjnZ3tIySixM/dYOnygj2QA"; - sout << "gubdj4tuvNpWh04VszgcZd7TbLntvd2IA8b+FDDzMkyT067BC51elULM0CWKoZgaaMglt3LqR43C"; - sout << "H4TQMRoitKUyfjAPE07UmUi6lEGYcrCyawT+Si9YQy+T5vGMpryrpy9gaHAmFQTGCJgit/0g/c5k"; - sout << "mbyZ4pilxX69VO9VHrc23YN0CMQ0W3pP/DpPNYQTcW3jeG2T3d8b48B+iY9boq4ONEGcx6y4wWok"; - sout << "SuOSzK6rTR834/xqlNGMLNZWSOFjLdq7G4oQLmVZVRIkUXrQImDl5W+1EjdglFE0jAnnuxjT6+DU"; - sout << "B9VIMbIzVlVId6xVrp6Kg+Fb5gcxGsKcpH83c18DbuuIoLsywmIEiWQfrSV7bPVuPKJVof/t+Mar"; - sout << "Q32TiRHMO2ZsUXC8w46fkI7EsH7mWMWOMfhkvnY3AVoAj2i6+orDHNwnTmWQiGj7FR9Aw5CQcCbh"; - sout << "W57bPF88lkQMxdEHBd0Hk1c8Pm+p3x71xzdIofXjTnCd71R8FIIvoduLv6XQttPFatTHY0CeRmjI"; - sout << "49yWBFJ+w+BSEP6cnTQeSo7uAOyit2S3EdLudn7XEBr9eN4wIVAF5lhti7HqJqV/pfqpkgaAQeEv"; - sout << "mbTG0L3+6Mr4RPAKaa1H1aQXcYT32jdC7w4BKaxvACcyjNuukUwhi8GxxXxIktPrOJnwNCs719MY"; - sout << "TbFkKOxloVafybwRZx5DdrEKMq+jKyCffePTaM77bvL1hNyK/78Q9TdEO4hx2q0m853zlnibtbwM"; - sout << "YcO9adPVjghDSU5amF4Ul5+rMxroEoaGKLW8+gL9DbmcW6Uj/vg7TRQohnGbzmKwPWT0hu22ID7w"; - sout << "+ILYIhGhG337Pr0Osu6m80nETubjTijMQHAb1/Pwv2eNOOdu8X2EM2awT+qtxqHCq6il4YFSbUdi"; - sout << "+i8vY32x0cXZ1EdKfgciuKtK180q1S57ZNZTCwe26z5eLXomvjtxLxBZl5z2TJyPEnJprnZWNOo7"; - sout << "5nuXsYypcrV3W2KlRmybUjRfc+/6nMKKBtU8U2phCUg1uVbzAXgyKJuGRmwxUOXXWeiS6f+X02yn"; - sout << "ZpJda7aMY2VBMEuxMRw7P+S18C/drO223DaUROCv25mTOZGKRI8iXQYEa9UxLvXgRfUB+eEGOuAS"; - sout << "7N4NVz2kfzGWlQdKqRoh7fWDkM6R8J3BgWhXvl6dacJcZyeRQGjpR0ZEKDFTtLK8Dr9NFYXk6Pnq"; - sout << "N9kMrQpwK3bDmdv++XISq0WHnf6SBJcNhSx74TUlUDBxsYAcoFefI1FNbFFobGmT7lEX+L7uQNmS"; - sout << "/TMxpzGwSeRavNmjkanqTTZFpWNvRXrfsod+TmJPe53ApSSZpiuOAedN0/3AGsVuqjNtcxf4DUCZ"; - sout << "13v1FAYWluQfSXtsM0weQ0q6ZvhM2SyX3VjNleA0/X/PPmOLLK423iG/d4m5+D95t3NRAbU+TEUp"; - sout << "Nff+BWEiVSlWCTwbP7mTaJPnAUWND7rjif1IIrQLL9IdyauKUOP04yMgCk/LQs0HsDkLxSexCMHZ"; - sout << "rK0S8Bh+/2V/dNF54MVJEqln+Urz2REIGmzqSjly38FZqDwG8Rn3dPYImGASFgGgrCAbYza3ZyEb"; - sout << "yaPzMiO8wN+1F+DbMDfvMtlfkY3fO8LvJ5csTKK94rSw2erFACsU0cXqThvGma5gOcpL1H9CwUsa"; - sout << "zKLfBclmCCGrP4rX1/SN7OBpNjbrCHZnkEebf2TrzxT2moacNtJt1cwVUVGhiJ/DYdZawCRUE8IS"; - sout << "IKDq/tSGREdGSMLEwJdXyRk+4LyVF1V+YF3Jp55yFh5skh3Be2lP5YY4+YiaZDb4IjJh7qxjjO0R"; - sout << "RmvES9HZTw0GhCa3ep3x8dvbHTp1NB4//onnMx9JheeyZRZOdEK/Suw5BlXNgCvc8UJJR4ZLtyuU"; - sout << "7HxX7orjWgkHNdix0VnJKSFYfn2uJ0LTPrz3sARjeVnQ3W0On0DIXqNe4thTZDwUFWUKklmQGV2d"; - sout << "bgVLPmuKff8DHP0xgGyiy1kHollAqwEUzD6bW3Tkrtu2f+LzTN2yAQxKkVsiW+sqEKqUtzNoXVQD"; - sout << "36tnDjAFotkirlS5Bzk5pXgDpbAxFnVsTDq/TcT0XbIR7QvBd0bXq4am1uoZjaLGA6bF0l3Ej6Ub"; - sout << "pB0ZMs2nLo/8EHNIgx4cBgXL2FN0DWyb2CzchL+jsPU4s7xBYuVvGA1bLaM/nlRV7DohA29pE5jw"; - sout << "MXQPYIDzraGVsOdJ0ghhlqqnwPgO0oCaGDxIlBehjzkJcgXPhWarYGGBdk19SUjt9y+aqDQmxShW"; - sout << "PrJagqBImi4S4N99hOHnIxGa46BFlk72ddnCvaJ4n5LahIZXXAhqOrxRiCKGKBX20/vzOh2ICqUn"; - sout << "MgfLtH5T87Mho0k7hh5gVFpYktOqOowwqPJp7Q7Qlwty07GrnkriILYvkp1bftSHHyepPxAAgaJo"; - sout << "jeyuGTtsa+K0jHc8WaDSZncIqmuKs8oVUZ/1YD2EkVlVXIh5NjyDc6RFfYhP5/w043WKp7xqeRwq"; - sout << "zaMTKjrUr02omLUduz14J+ka9uUErd5O2xPaV2O0LpLuZaMjoZ83ZirW6U6ipV6d7c0B3kC4CcVk"; - sout << "kUdzqBoH3VVbTav1vugceepwBjBiXsRqReTJBzNRJK4PPu/ApBKQKp6jyZoWCfhJiuJNB1sEjCbc"; - sout << "o6XEZWbBEjqRaCujvVzo/dLLGHUUOXyGA8sKJq/iAM2dqvDoaduLBnCsp2oBIFw1q0s2psDRLdvY"; - sout << "KiTAKEAypmqRB37gV45Mrmn0zYeDR2/EsWDoo1HFu7Kh3FX/y2fL0155dtiBJxkHDJIXj7POIea9"; - sout << "Bl/qheUN9FwqXsVheoacBp+Uh9NulkH01NsKCyX3nV9ec1sdzwh8hj3zWZIpD+ATVH3S9Hh0pt2+"; - sout << "FHzfu7z+YtwgpWfSqim/C0/yfDF1QoRws09DqLTke3TmnvxoRfkdOwsc5y1s4YR72xFXVrmIRpkW"; - sout << "pqHx43/MxHaQ8WrEABai43+FqGJmR70fvCNecLDPm0ZlGySkOCrfgMH9lK018QD+DRu5D3n6alPI"; - sout << "3bEqruPXlP/N8eNnJFEeDbxQ0aQFJFxSOmTkYWr3LZBjtjX1Cc4qujxcCaUH2hnKvdyxejlt8fEa"; - sout << "h/I2aSaD3ejPS2xz8y5D7Ve0mxx6znMEO8E61RbXztaIRzze57GvfwpTa+NbAjtX43ltW48u0yeu"; - sout << "5UM25BNbXDdmfUzzVFpiaflImZj4Ga/k8TjlTVURwRXX1GzB7lfdEukFRdQnY6VQcsjzo3O0gKDA"; - sout << "+ZUtku8C2JpdWVYYsTVoyR+5rNlgHdaGqbqzADSrSqNBBs0OLaEH+pqoVvN7gNNk/hCHN7tvmi8n"; - sout << "LZQHQ+0Ukp9p3bPTUapH+h+NJ/SXUHdjHrJpaFJR+ANmN8a79zacX7v09ig91M4yRTSxzMzsbQef"; - sout << "xnNKm4+2JxpFLH2fmM4FM+Rx30KcqLmUdBGOaJYNHQqBVJYYMNVfdRCo/KwBX7r+V9SRlLQ5ijED"; - sout << "zKbJlHi7LyjYOq0iv0R4MIiW67OR+5O0aHeoXQ5RJ6Rs1Xytaiipn7aXphPVPo+fZ2xH6nLTQfFq"; - sout << "fEutvtOrYIR8G0dGUSYsbZEhdPC/gpSdHsO0bOJlxgXhNGNeOFtYohwIKP9Z7Giv+lM15GPlxnm6"; - sout << "usrSaqlZWs75RuyOn7etmGBSg86+J5wE3ihgVXcsprkgs/y5mnP9iPifs1UBfDO+2L6UnmceygSm"; - sout << "sIOIuMzGUkrM6iOLYHtp15txD6bUYZsIEeCCnjaIJujQc0lvbU9bh5AVae/HG+SfuWqE5nzp2TD0"; - sout << "jug6A2KoPK/ufGzjCJJ6VIe9v2bG3WXApQGWyj8lWCO+d6YtKFA4agCPTv4FSnS+Ip3Bws18ZKxj"; - sout << "M+AFJKpr7lmSJURUl38hITa//w1TKSA3vSoMaEFGEH8OFkOCDFOHoGK/z/ievfyBkC0/WgvwcSbj"; - sout << "uvaOd+M4umm/BCNqcPjZ86HSej9mSb24Kl3Rn2pxywK74b8P3z41Z2yVbRIjK1Dwvo6t0zPp8vtD"; - sout << "QHW/9v2uZ0VhBdbRAL2+qFD6qKg/sacjR0kE/o/zRR5vsW4K0efFZ8uerG5hLs4rYhZPtiDOHr/M"; - sout << "xOGIfBpL30xzMdMs2zTnbXF7wECjcklZcwlefbO5ubaT0doKkVSZ6YkCQqVXL9LNVNHqULonUgBs"; - sout << "Xrbe3KKGFrToxCljsw4+3+RUVnNN48CklcKfEfTHEXoUNZ/p5msufJct4JL4K+35Ecfauq0GOsLX"; - sout << "sTkVaLfkelv8Uiju3VEggHb+lLdrY7oKT7aC+kyQu8SjtJQY7b2aKQNELwrX9u95mPNZkySc5vaA"; - sout << "xwL/6ar2Y5/UA/Lh+5d4hGSkQ0PbSacBStFaLzJ8AVdOcXihah1a1ddYNtfH6kNsck9XJqz9Y4G0"; - sout << "YdFr6DGHNLBlu2ksVL0ta/tfPZy9BVFo6Vz3tbpnBwkPJ1vo9EZrJxo5GJ309oeLz6mYG+0ctXJr"; - sout << "PC90nYz7s4RxXkWZBzq3RbNiBGNgP1iOU3x9INOf1CbKBhNSp3b5QJDfM+hVzL0Xd78pBpO/XWQq"; - sout << "hdl2dL5XZtd3XYslKjHDaZQyZgnaoWD9hhRlk9lhweUSB5bFjHOH/FZjlAH8et4Eri7N9Y/OFHFP"; - sout << "+a+FoLLT0YiBGFsJiqe9udoECrxUR/A1Yi3Wen2xOQv+eMMQ6IcvA5xYONHWrS8WgMT/I7oELwnx"; - sout << "GK3kwjKy8lQJ9HOmVa0yuQsV5bbrtjQcP72TmuWKOcyNWf9vHvLbWSZUDEXchz4HfADCJEehJzh2"; - sout << "vIYiVvotktpiC/MzHmHoTw0Sf/hOT8AALmyPqVFGrkZHhLU4GZTqcJEzBNUvkvuwo3qMHzOc3zfk"; - sout << "VtoQHlg0pOcKczFo/44eMRlpf0Cn4KTcU6vEmUncnvx1nNMHEYKQNGp/IXHpXp+PN7s9zFDFbQHY"; - sout << "iXURtZZ/cF6jW5TshrT/CgpldM4oi/gn3yTm8AJoqATWeF83ft0AphzHDfz+B0qf2wF9Te8adqcb"; - sout << "ekFiLrNf0VfA/wMCE3drkqLhZKO6L99EK9SZqsly4Q1rwLr+sIgOrI5v5g82DOFpY9zL8Sxvg7Gp"; - sout << "03KDCEsW/2eFfxNtGGZ2P6JJOWLIe/6Zsx+1F5lcbYxPhSirbDYp+1bMZvbm4uLWHttHS6e5NQH9"; - sout << "pW9+qFTlk68lJWtpI5Nzy2CutML4mI1uVDgIctZLSHZOTlWW3LICiFxYVLjvE7JExUZMI5J+3VZx"; - sout << "OTTeFGiuzoEveGJ34tOMrHxoj3dIMkRN9kh2dN10crD8MKoo1sb2EqZvhKu3yb+XxKKl/RkHTuBv"; - sout << "JZjCnoTnGa9JltcMDgzO6demBWSN9950HXwS0cZRcDIgG+eDQnfSfHGGJ0UuL9ydA0KYWYAPxVMt"; - sout << "tqR+S8lKMzk18k7FuGBsFjloOSgsychm0GSTKu+AVUJJohpnT5KhE9Z7Y1iSc1pBOxyf4iS+FF5v"; - sout << "AzB7BEivwSPqcjPplzSQ1uGNZsa/mCfGy77XBBm5wYUpKDshd4l8mOHPFofPW9WrpI4X2e3LuiMJ"; - sout << "8BijYVutWZv/8BrhhFuUnngfLsaJBhO+KL1m/JsQEvWIJ53jPt5qgEOn/4Tei953J2hanoVzKelM"; - sout << "l2OhgHFUxvIYGvKMwLRMPQ4kUH3C1vvBJivvyIZ/JWcHRq/0CCjvg1aPUXSXhBrgkqFwQduoNW9G"; - sout << "D3BowPV9v7Mk+IPddFxHdQeyHdaquVKxWoWHzlDnv0gbzlv/EdZVUd7k8rbqYgUdPl9os0dbh8gq"; - sout << "nRpOW6MTBdFojgzrdOHPhdHM+Rs/TIntPPd2YmryOjdHhAyrVgFTS/rx+yDRrP+ChjTPAF+k8lG+"; - sout << "qvN+Z8gDFq3B2vfIgAUs0rbSMRfOahM4+EH0pX7fe9ylY9yPHtWKs9i3mU+uVTgB5ZtRKuSKkpnO"; - sout << "MgWOp94H7Ui/tI7b7hAC673dprCx1PZgdIncutPRIlxzv8exEVhjhH5AsW4gRNxLX9pukC5fVdXF"; - sout << "nWif4xcZbEcsW+mm6IV8NE7iokkhEC0bahxpHeS0H0RB28nZY2idhHP6hLDMkSg9b8mlJgqcGBtj"; - sout << "Q2Vd396H38VydtreELEV2FgC6Ski/PFP37rc3/NDStzcY7d8twoEiWa1msraDn7zlvORuk+Cu/Ec"; - sout << "VaHwuWQdyqf7dhOhrH+/wK0RTPMKHDyLxkwJmO31Ka0LEOvqMcsUzEaxAMUNz406l0r6pLW2MbP4"; - sout << "AdGu/pH2HwxZ7b1ps+8JiOu+U3SDrmhMZH7kS8LUhCNpH21Ty6QzgJcaw2jC9HYUoNeobJSQaOFu"; - sout << "kiOHd69hw7VbD752KMiVur1AYmRegfhkEm9850zS/AjT+SntphKTtFNsc5cR074M9iucA3cH6Dv2"; - sout << "AUSKvl15U83cbs/B8NA/4iSp1OTEb58O97rM0iLsWyBLSq2Lx5ZblFZeLi/rPkoVGMo/o9UV8pNa"; - sout << "Cy/ushHj3CMku0REQZQfcxl3OgQ6U6avNywmwdncjoDCwX2j7MwoZ9SrAKvxSedrSkdRhMvVkuJO"; - sout << "l9c6Mp86g8KafJQIp2ZS7CMC16uoiaUZCqdcdIEsKvzMreCoM+XGxXE589e5BsfV2gTuxpvnWVCs"; - sout << "8BaKUgLnLKCHzyioUVC7bzz8Ov1/Wqa1LHzsrobPw7DV+dIDHqAS0d7TRra2q6rXAqYA/0P99NTp"; - sout << "uhJkixu4ACmGbuBO2ob+t8cdczxIBSRPHHZNCXQG1t6A1IYpozEWiFjATpoD+gEL7Mv8AZW7AF3Y"; - sout << "wFQEt2JapH373jpvJ/lAIt33yTMgyTQHKQ6bsTOWpK1EK8NvnOSOeiFfmtseS85l33K8vl8pyXBL"; - sout << "Fx3dqms9fdbwhUmUWGcpzynCrKyV6DVGFznQtpr4QCl67Oz7RaaYXGe+lOmyrBgrnjKcQk4ue99C"; - sout << "ucZa94y9SaRBTFNbXtpRm9jjdzoDmtd0JPCGzSACGikNArk4gC2AVKMJpFdJrS3e11Ew+pprc2uR"; - sout << "fq/nf3C9WQ9lQ+KbpwEokeXHWNmWBwDZwFCf2w41zxYqjTu2XjGt27ea7nleb1IA1PtQtcSz/qoC"; - sout << "6A7G4EFk2WE6VNL7/Wv81DjrdTdYy8DFQBzab3Qc7T0+T+aNZdNfoZSj5YK35BB0mmPbxw2U4sfx"; - sout << "1nahYLG8OEQgoOVG/3vG+EmA+iCAcCBHPObthul5KdW5JXwPw3AMgL0UL/3GIpXRrzE4JTPrFfzX"; - sout << "zFmbikWuDkDw9sP8UNfHAOGWE+OorN/Y43rcSfVA6xNgMhqr0pBbvVTIXbeIy/nR34IJGR5xN6YS"; - sout << "G1SriivqytwRmJLC+Z4OoSVtqNPzPvUBzs2wHPF00gGAE1FpUa/CfxOfXhbx8VoZWMcuysBgpRCg"; - sout << "Xne4u7qvre3CRkuXLazM6qKDt47OA1is3gG6ZA/XCvcI8EA61YccN6zTpFNT2cBGQNQ11tK5jqEV"; - sout << "JeONoq3L4SCF6qCiCKMg0nsc6sk/xJ5Tam9Rl292lzxTV1Xa3be+lfSjUdiWnmbU4ml2Y7FEVbWv"; - sout << "4A3WHted4mKb0HiCz8wgbHrjTnNQyIntFcXuNAUUzri4nGsV128EuJrPmbYXqWuKIe1wPT2Lbco+"; - sout << "7qH3xg/zxl+t/bqlyqghqoYQMPw1FJXz7Z1TMI9TNbxUtCd+J+mX3xkrYI5AtCbTZh3lMvbwWJZa"; - sout << "4AOY4WRavT6XHsBs7hKl0iHOmTyMe1bkM6sg2BChZ2B4T3BoeFISya1Qn2U3R0Y61plw8OFrTsAT"; - sout << "FKgD4pSc2VaV5bOPM/vqg4B1In01SOw4itnoIC9N4+IvHvzQFYzeqiueAcHYGss+uaTMqqc9c+gz"; - sout << "cgiJCv480X1iiauIKK1Szm/06GiT5WWnkyqWEqxmNyMPrj125XXg75AZeRDazTWXjObzZkJ7UA6i"; - sout << "YQRFCFH0enKb75qIjHQ7635m/Xegoboat9VCJDGGVNVG58hQAVMAWHmnIb6oK9t1aaqXmAYXXgFo"; - sout << "3uchUubvOjQXN+txW64ZRXzM5lAw4TklqmhDjxruXRdddoBONkvSPSh/XiDTkdmO6QKTCh/t6NLo"; - sout << "Jb9Z7nfmQUd4adkS/+aUfb9LNOPd0GHxq2O/y9XXHvRTBASUV6xlJzpnOBAtTJSDrbLj9NIhiWpF"; - sout << "nsQ+Nq9aEthi7RHvc6rujDGF1gqfJjsdPUTxveNk22Xc0fe4Qm+07HEg0HvhfFWJdFJJHirD5j82"; - sout << "bZ+QZTppInMAQRh+dxZhlCzPKdqS7WPS8dpXp45xBpYH4GsG7SaRgbCBm8R96j5iSW3oYd+ql99U"; - sout << "OC5fYGDbbGT8zl+mtYi23CAV6AY/izASsZHq7KzSV5taKzEZyJXf9D+wRaopIZ5JPPtmi/fr2HAC"; - sout << "XyKoAPNAywLXUTtDezuKTJiyy/DBlHKxloa3uVTeG9YBJfiloyym8p9GsCOcz1p0s4K1Oyph1NCo"; - sout << "CyklDAHvlMNjTA4hkWIHTyIStVmgvVkNN93qpPYYNnV+fIETW0tvJDnyJ2OnHVP4h4VBWbntk+uE"; - sout << "JDG+DLleC5n87Kk+e/lurPTik1Yqpicwe2ct0AgA9XJCrDO6CYGT7QzFhEujgOYqzUmFe+KGn9u8"; - sout << "4QMY5HbYXY1P9V3pR/QyoyxtuZNU/caTzRM58Hxe62q7BHmsL53db7cDUE5dYVR3Lx2uK/pm3QWH"; - sout << "IIoyUhcLmk04dLwoi7YAhnao9IJQ12KlFISFoa/3WvhWbb5M/GttUDXGX1A9JuGrhjzXMIfGyS/u"; - sout << "wugpWw/5SnWaCis9sNGmmMNC2rniqemMrjAUv5mGPYA57KyKzvVGGKK7VM8qyrtwSKOQ7fWRevDE"; - sout << "NVAwhR5NnzdQmL1x2UrFcEkARtk/8RU/tfy3nXqg6Bi8g+myCn4hH4BOtK5E9GItI4R68YjNDVBR"; - sout << "MiW9KS5gXcigTV1fXrChyjn4Enh+GrdLBpWDJQr7MKSVSmqGXntmhyTAD8Hgi111whRmKXgwi7OC"; - sout << "k1Jn/NAURbtbi18YCjCc/+NfStTpuMv01C7+iZSQbO2rzP2aqaYSSvIx2e3/e1fWfZfTJBMQzcfs"; - sout << "zITZqhDilOIDxPPYv9xAkBxnwGXIkPAi5133yWC5gcNh9hzEq146cKvQ8BFi1RHGL/yOiDjADrns"; - sout << "Ky2PGu1fBfFNfw7tqF03f6zNB5IVWVjmnKtXhM2bIGqI/EM6Qx79bcYKYXcFg8yYbTc3z9KAcgp8"; - sout << "es3R4Rwfg9irMdI0DK1CyYnpoa+zWhUXUzpTbSlFx0zZ0Vfx4xnPh7ZJqJ7/U/QNAU3lUP8LXVAP"; - sout << "+jMIEj/H2mx80xaTNCDp/Sl+QcU1jZYx1Ycl1B4gH2/wsYYr59q/mDD0gE70hQpsE9ODlZzx9hoE"; - sout << "HFRmDUTI0um1u5fLykqAJFVPcfk920vT5TqEZSMX0WvMVxqvN92IUxy2JNpXXMlBiekCZhEKKQqg"; - sout << "D3MqZCUlGFkFpO/zc35vlTBUCuTvbcnak5HVcREFV0yNbTyukhEJzcdZArcCZs0xIbM873u1y/mZ"; - sout << "+dUcW4zZJ4jNSa/Vz3W6sj41hSQKrQfPyeeKpFd5iKxJPXDxV2iuv+ZBgJi584KVpYSVOUA8Bzi0"; - sout << "0kQh/NO5BdU1qaekY5lfrpA+O/tTzkTL1ZslaaBimJapn5KZcTtsZR0YmrE0E5okd4DxUccbElFb"; - sout << "81SuaH/YCvib5kUbkXA0ooelAU5UmmMCZ1ArVVgwlLti65IqtSWD3LB8rIOUT+QKutHVEOpNMOrJ"; - sout << "A8DnPVgF2kmeOngOlOwb8Hgpg9I9YCuu2cojbmROM8BZh2V6E9oiBTeKPgqgZ2tkgqGSf27SEpPo"; - sout << "xHPyKMhQewkcq3WQ132hIb0s96PdEfniPUSig2MLn+5qBZDFLHHuB6K+pl/6OF/h7ZaAaCa4NCA9"; - sout << "32ecTvY2MzUHmBM6CI/QKLRtBjmE09TB8s/5/X++k3cde6xPE77c0/G6qLrOUKFITVb+iLVADv3O"; - sout << "zhj95j7URImm87lmo9ZBDTm6bbKoQtNAzEpBAiGs/VDuFfA5ZoQcwTwEnhmOs/4bT4591uwosx/T"; - sout << "G4whvzgfcbPt9yHc2nnSSrIdYTSAwFZ1Ksh36RgvDXxaOHozLvlduvPaJkYSk9HijbmULXMUcyvD"; - sout << "6IrzOnn/8NY6m++hE3KxgFM+hlrlAp3qQBDEGj5FAYFu7AcCT2r7YB5kiuwjM/7J8KyJYWg7Z6Nh"; - sout << "zJbYleW8QA54s40d7D6AmOBX4+5Mx4e0roOM7WEFoqvXqK0ebAOrgwZ7MmqtGwe+5/bqkfNqg06m"; - sout << "o0UCVDLmF/SzqAsxgIJt1P4/l8HzWtrFINUcBwcDY4ZhV1fbWICOmej8aBMJ4j/iUHLkxbnzQFBr"; - sout << "xuNgC5/5I+iLjiYTd5+bdsITz2aRpXgUHx94KdOtcdEJ7uLjUeo7jeYvQVt50x/V2thmd8zHebNA"; - sout << "6Yj14+3dHQH8c29EghfihdNobQHSyw9eVo8RyVI40oUxykuk65ILT1mLbnASJcKIInvh57YMVTRv"; - sout << "rpPmXm/b5ZUfTMbzpTwO5DThvD+TTGvLY+eC0awfepSJj9CjiBTqNBJDbd5yKVjH+oM0wTufEUgP"; - sout << "Fr0fondoPNTB61iwLzHrGVbzU9Rl8rsuiQOr6Yz/Dj9OjgzyuvDb3zMilAEcLydUFmkZlq3uVCl+"; - sout << "LTYgz2tAQIl0+6SWZCel1bGyRKY50U2bJO0dbHawgGrUgZ/OO2k2HJnC+0+WKZeSj4s3v1OfCZbq"; - sout << "F1nQDtk64Q2X857gH4kTx3MQ8gZCok7M+sd3p3eA43nUuis/SwComUQODyRA+TmuoE9AdASWTZQw"; - sout << "Vz/To25UixKQtov8has8TRMEPOWqtZGHg/1tGGyZKkTbHQlulKwyb9uusuzC+FAQUEtpTcXkQzDE"; - sout << "RNHKa/I1/zTSLVnaADsLgrQA13Igh5w86Bpn1bFgE8iEW5zQoNs6KDYwsRQ7mMiJI75ylVIvfbN1"; - sout << "Cp7tpGCKCL/8xtWWII5ygWogcGhi5abUDxG0WQOaYk3+kPU6w8OPpbaT7h9TDO94XhcwUb5q11O7"; - sout << "LAKIZY5+/7DhdVfMnplHpLNftDihrGwf8r46FUrZ8MRHAREnHSsch5dYY3boGxsFoNLc5r+fbJVD"; - sout << "TxslZ5TEbJCFrBtFqbT3xuLReyo4FsOPZd0N3ZtFXt/ChTutUudGtCLfD3ucoJohZuz0Y5Mq5sHQ"; - sout << "Kb1vAx9sRwpFnpO5o+RKYaGh9QkdrPXqUlcyXkSrXeNVfGGyET2vZB4VtQEAlG0nF2dkiF2uxKbc"; - sout << "6swYBEYWLaftOzle9xXK6NrwQCrH/guAo10Ct+7uLmhK6P5MZW4kxyLi5nhOPkB/jiASFRPN1NOw"; - sout << "KdNeaakEO7PEKPdcIhWjvw3sePbhOyHnmdJGHRB5WUvjOqD553+DFu4pgpUPHx2PV2IfEF2Wtfvk"; - sout << "1J4pPO9DvXw5kBSFmoR/VdU1bGkVfkDMafaHt2diRtBZwbJb6XgkzJsuSiEtzaZ3BLaieOYJKCFf"; - sout << "LK0/Jj4SEEDYj9NIHq5MsWH9PyVYvV3+IpJa/AB/WHXF6XAMOsLR7ulEmViNjv3UYOiWYEyT2v2o"; - sout << "hYYg+6/lbwOg3frwE4tHu8IMfIZZzoFHbfT6MnP9ajoZWuSbbIj5KwvJ27wLUsQPevjbTypUXIEv"; - sout << "W5WLBcBCegLZyIdElFV+1kJmSF5dZhY9RJeDOs2/CpgRctMX8hUGtMjLIAMjm2yzS6FgTtuuPJdr"; - sout << "ErWoYhOWgGSZEitvtLZn6tGl+JZghT2F7dfmMaJH8y86txR7YCaPQgThH1zCh+yE5hvZPXW7+OCj"; - sout << "D/MUIXp648v2+9ItiAlAZ+ho+JzhIA1jgC3h5fWWmEc7RnWvtyiatDFAGo9Z4UCz1qtRqoKdlhr9"; - sout << "loA5N8Q/E1vPO/G3X++gNdEU6/ykRYyAODNhxYTnkiT47qRUzqSgOwsi6ePyQVuLDbSCwcpZK0ps"; - sout << "qR803wEQBh1/nA3ZUeO9g7ZGaybxNoTj6Qs0u5vRNudI6OkjlUMMTdXVWf5ZyV338dihwHho3RQT"; - sout << "Y6nYvFbud7IP9HMA5ga98s8z1ImzGbKGTMqy15rL5/PVkyddf7MArGuMSKWR4F0njVU40ILJh4Ns"; - sout << "xkuzzPGZHb68JdMit3OazsaLvkZ6kBlq3KwWH6BISfiFv8eLYRxCKSfGWoqcw1qlUaFM1MQTJ0Zf"; - sout << "4IK+R9+6/GGQJmSEDCyK1t4jbWDr6pjoXOIsihE/hsJj+Uy0mcQ/pRQpxGc9DvL2/7rA09Lr+Hk1"; - sout << "xmm09tLOMEtd6mNexizTXHotFeN0+M/fMRwjIUY3lZSOHFVhb3AcNRcVE8IhsOnrUu9SuZCP5cLI"; - sout << "xpc66aGsMww2jY/TEMBmWn5IUpX3vNe26iJ8kXZYYqPKz67I1BmH1wOXBaHZVznvMHXBMF7WhwhN"; - sout << "gq7Pf38r16eou+L80O0aAaHE840lzt+jynl/vNb5tZ6F75G6jIPW+ARUy002bD+n/k1lnmwZ1WgP"; - sout << "WnxwFloLan9gTO8dZG6uPyDHL8HaXU7cpty67AFbDMnYnajqpixUyDYCZeeYDvcCv+lqDm3YgKPx"; - sout << "XQPEQQP+qpwvPcwLIebl3F15EhdJuy1gdwt3Dyca13/+aQOq4Rd4AYFl9U8ehlJsrUOus7Q6xohm"; - sout << "J6wNJJzJpnRwNH+qekXnqKqRdTUwcQ8JwcN9u4sUE4pmBWS4/hJSYHpFVaUbMPmun0Dou54UPzod"; - sout << "HFGXXZd76cZ+1MqaoyofxHU95d3rh/47EjPtQInAKAXmQlud1wYLjOhdDwGyvhPN3XfohEPrH+s0"; - sout << "n/tMDShXPyFIaS+WLQZbaid5LuarlwA1rQc5cJ16xtSrW9sRjpEM22MxGmCyyuR5mMhbYLlULR1R"; - sout << "b9UJ6/BbJ9iqO2LOh4geqx7Pl+kvdiOwAfXLP3mFZ8PHTHk9mJMa1TlA/fLSo8O0YqHMTnFe7OtW"; - sout << "qMNzs0Pz4ucJ5uGzptFUXyCqq4vmSS3Cx4eolZv36mgGa7Ll2TJDCJckb6m3lsroRCTs6zF4B2yN"; - sout << "VfTekTPXttFqhi4GI85Y+ZBvMoOIAsvysWCsV31AzKg/qlb2+dOumyMScT0GX5+l3OMO0iqG1QXG"; - sout << "3fJZOpDQHq07WuCisGH2DOO1sEE43+d8fZHb1l7PQ2w25w6hq0IA8u73oDWRyTr5IqY+oDM5d2K4"; - sout << "Ysuzkl+odreYxlUvxCjwgG1UkXw31DQ50xIwpChB8rEd/5gW10lnXK8XTbVgL3fPuy0cd74toIhY"; - sout << "JFg1jyA1ZST25w82oJBYq8f0NQtHc7bK01cS4OM0hmOGd7PDKiRGiIrsLcjgfw/J/jx3mBuEaR5J"; - sout << "X0NRxcrJZN0RdwhmwfRIybRGdSX46JVUCrf15yD6vFi08cwkMxqpdn6fDfh0BTXzhmdJU0+219/a"; - sout << "JFJBFD8iktFAl629X8denKKlc2sMyBT0IODc2OYlS1+xn1t13uEeLar2l/EgG15/eeBsELvM+M61"; - sout << "7uSI7t1szDmeZmWrO5yVcOBq1HFoEmHIlDJNtYQ7sOqRiaJehiRNhnj/9umPDOWCmBdnIXYAxWMB"; - sout << "TJXpWzOOeRvpFNXUHN8MDttwWjx7Osmal/5ZqIbX66+jdQrLih+L4//J8oLyCBjawxIj+4BVLSpI"; - sout << "/jyMUEbzBmRM3HiOoqqgiTFlLeRAM666O6oGhNmrFC/Zf32boZ6YmU0OicO1P1zsaRLJkvMbCYaU"; - sout << "Dmi4reAoKBG1PBqaWEhDE2VmjiPPCs2WqZfll85wyGQDgKKdyboLm26UWwW44oM698E6stYbg8cv"; - sout << "P8eyOR8awT6WwPgOS3fJZFuKTgN5LXJxk2BVCzx1F3gnmMQ5FHhIVm1srDDFIIwSkuP9mgjN9lEI"; - sout << "SGCLsFYdEhVedNZuOWMYCvV4RChI5XCPh0ZRilVbCP4kkj0Xaqq3SbNWD90lwwJvFdIkZOEPeYc3"; - sout << "xfLARYjHqHbNkYZLpnTw6BPH8L4BCDl4v7ndyl5Ef5zvShk7L2VIyKUK+3lboiVjwDoI+k6A7iNC"; - sout << "UW5f3fTA9D/s1hxS/LxaFU+9Z/bOMjiRaPLQ/ioOkwyrUJpsy8/K+T78Vv5XXqtOct0aXrtUydgP"; - sout << "uWZKhAFxJe5467wlC1aja4hHwyEeQmn0P8ilBq2G2hDYGEnKEF3XN7bwyFS9RGFdU06jkKlop8cm"; - sout << "s9bVHFI4NeItHNO48LVlRFQaVdq1p5vD5elrH1h+J0s0BzDauJaraINeTlF/PrpGzdxiA/vq7sB2"; - sout << "DHPBfrxe/yTTu3YTnSzMYR5mvPdjKh9mH6PmzTYyu/lUN780SUyW7mmEqoNT3LcgPg6QGUfg7hqn"; - sout << "HP7ml/WtBRhZh1eLok4o0lVOhX5S5vMkXFa7K2pObSkGyyItCW+Kk9SkH2BboW7aYomw/3tAd2ce"; - sout << "FUm8H6Q9NP8KDpaebg/67KZijw5kCSH9dBi/XzfZY8vzWGPW6Uj6zpbcDfRQDb1jwrPKZRhQHHiT"; - sout << "dU6ZscvJMqSdHXsCNByBtKrUX37K8C59AaEmCdshNlaIOQLvceIIpJ7XTfmtnjSSvK0zJzBWt+xf"; - sout << "RdpAeZwDNK66MCl0DIJwG0aRK0nwuePf0f2k51xlsrnl5nY5rM+hfblfsm2BKH6BH/SYlUSLxQlC"; - sout << "MhdXA3MGhxN9yuHCm0m8WVc4/4g+SCKnp70ZghQORzaijlU/0KS6P3e1po+y554gEqicyXXndnwA"; - sout << "qmRp58+1pxbwORmCQIdgriAZxPQl77hXxCMES9WQkBVb0RKNnbWCWvVdkwxxYHBGhO+m4kZVjFwW"; - sout << "0jxlEcKNa7WdQStRXCap2VDFA0c5kUS7WN6R9HbM/u8xMNLzzE6jrH8l2h8QrLkjHE2R5y/NIVAX"; - sout << "G2+cQp2X9XxHVWQmfflpXPKcDGGYR70ALFrgeRkQQAxfR4WBDLLQitSat8H/q+yyl6hdGj5nmYXW"; - sout << "BRy7URAh0ibk4K/Bs4d2mN8ivkxahOB1wPZS2FFHP/I9C7nikHmQwlg4VlPJTwLp25JDsOypt2sH"; - sout << "J8CK02T35fcv89Dg6GKoIp9RFkJ2RcSUXyWP3JWFxH/u1rdTmOlVP7mAW4MgT8AQdXWXkfaJxq4S"; - sout << "n0ZRo9RT68RvAcrdv2z6Hwh4klmlRw9qFtokZMGLk8BOtPGSOU/Av3k0RjEsqHLiQaRo0JpnBCw7"; - sout << "JcdRwIuqvfWRJMpGF6DhHkIQCFZcTSftnNDKUBLVBn41O/VnUCakShsRIy0a3hjQpg0M/7hFbZI1"; - sout << "wqiEu/aR4jZE3wixAJhXFC86bb59UwlH6TZ1Eszn7eH0inEqCSlMMRKTo5Bz5kybjP8As1pkPS4Z"; - sout << "/A8L6SnY/HIm6jy1MiZJmzdimBLwv14JNZ2oUM/TWMzgnFayE0C1dwiX+75KOrJqLl5hnM/5r+Rz"; - sout << "dB3emOb6i0OQI444QHxHLK6ZkLlJr8kY5opZlAfjyzuWzJUG9p1CXDfL4t99GOoq8TebYYdjmbyV"; - sout << "BLn51c8VBQ63UM/dzKKgL6n5aUWAenJ9sNV3/b+fmN4cPWAv6TZQTDt6XZxPz+GT74rFhMv5YcwV"; - sout << "zkuiYPllmx7WE0pegs+TV7BpKef2DBNlo6D/7ELNsr7L3+rz6w3l+m95i8jtIpeoPRzNxMWfvFZw"; - sout << "OP46naSo+j+8Euy3CnMnx5HQVwBpEeGbqjwyhsKmRyev5wzgYGe4mxNTgrTKsVh4exFOYkFJovWm"; - sout << "l9VKTRY4/Zd0H4dodHVquSWT/i6ogNNwB1O4T0I8hs8T8i4XybD4hM//dNvbPxq82HobB1R77rqz"; - sout << "7wGi/X9MmX3ct7FIpU/sYUT2DeHZ8QFMfiBhSLqoHcJj2RgNr4cX9ZbFXk2nzw4Dz/ggTDOx8XRO"; - sout << "qLovxOk8Sw7KXBDVmW1/kBhLVN/yffDJjzwwk/JqiUI3kPaLOiiJnnLRH+fS52mC/+M/xg7JHde/"; - sout << "oGmlCCGQb/1q2j7he+nBCFZM8F6BVHnlA7DoGCzSetTLkOLo4OZssfDw3OSU/2vxTxkZ9qsarMCF"; - sout << "uIl0MrtO59MbXtiMtGS5gcZL7Tu0SMDJUQYFtoZ6+QnDgHD3P3BsqyXfjRR+DTGvcVSyh878HpJV"; - sout << "3R786KM3ZwW8iVxFabz1OOFx0Ox1fCspZa3L1ckftSTD6ypZAN4f5ox3Ad2iY3l2J7iPeE6/a9sk"; - sout << "MnHQmuMy7pMum4VVepnd3lfqJljkCG5ptnznCTmp2wPgkqmvzXl1UU7kyYrdMt1jTzg6fiKsbROp"; - sout << "wGSxXLbRGT2sFjU0KExb/rjG21a0NVVp2PnYMQuyF6glGmCeSqohLsV3nv7eCbUd42IacVWAR0qN"; - sout << "c+5Dj8hdAmRMU5DJrbaLpuHeg0gouaBYrmCYGrcMqcI77VLyeQGF5hzq6t6IhiPk9uLrW2tpF5Df"; - sout << "gHr/I94WMSy4Wf0RnACjVxSeRewTXJDuA5pKq0EqNs2RzmiB7LoktPBzqBgMjCRyKlCAzwZ63XOt"; - sout << "vOvKGWCbjXcVEOZGAY3s4lQRiNIJ1Wat62u0MSj9BERVU8kd/NWueMn+tbdGTSR6Hgyk9bvHF+Q8"; - sout << "CdFQntiNHHU2Qx5ZbzPPoNNjqwy+FGicBBG8/xV2bPd/JWm3uo/8BFPqzORzGAnG/BHSARPbEvkJ"; - sout << "ix1Nw+gepwJ/M8YNp2aE+hYTBUBJueaSPyPR5FS6ocR5oLd5j6biPlTv9gHGg03Wc4W3BqrC4/He"; - sout << "R5sSsRThAGj/wbDu6SMIoT+2gUNTbUZ7Lx+8rVcWGY4OLaySvre1BzyhGPpNDuIhmbW7n2jfEO9C"; - sout << "MYbtPL7skgqV4L9JlLup+vbCNC5ccOpxm0Y+XSA/Gidd+aZu9D4R9Ddz66ns1Ida6bWLFM7T8BpE"; - sout << "dYgcQBJ63ixc2nYknR7w5Ez6JPnmfUSDjXTrY+tnXlbyWIrF1fI1Og7V5i0d3G2jcuitqs0v6SwR"; - sout << "rgEDVnVz0O+cd56TOssSv1d2KPIY6tv22oH9P1sNzjBYN9np1jRH7e1GpnoEvY7MA2gNyBIWdrKA"; - sout << "uUs0c5gYr88/DbJvZtSnmPUMRr2Lp/RtOHnabXkbAevAovdIvmh/BfnXnSjrOkH+BH3Ici47ohO2"; - sout << "Da/7OlZj4wAESZO3WO0SKN4fUwC6M6LUM2L7kg1tvqLA5KtYokFOc2dMSRs8qz6qpneGUv/f8NMu"; - sout << "PphJtFjOixUrhCOZVv+0dN94L3dNPRyjXlDp4EejgUxWdeYVPxiyXor+LSIP/s4yPNmn6wIYWF4l"; - sout << "hIFjh3drkdxwsaS6QWeYL3knA6I2rR0NERayOsVKxcVftkonwoZJHpdRkhDhBj0Sa8TicGcSgQt5"; - sout << "kqnjLvOqNA3A4XkXj445r/HiNWVbd/v3DRYn0lh09HU7ihGo/qrC1DDjIQjRm5MO64mM9xZY58yU"; - sout << "2fHWbglRconR2QeXmQ1D71aEvNZwBs1G6s1VqbCNgpeA+7kvjlzwFzyvfIqMuLTlvUo+4uCHf03O"; - sout << "im1AxMxq1zvmRPM844aIenx1bhx4vKmeEwTeHKYvjUkTCMFqOXxq0UeK5XSKJkXrK0F98MORKeEI"; - sout << "lhpQXL7ky8QZChU2k8DDBaocWzF9oRtrXDY6vfuvcituo7gbfVIDzQrp4ran2eY/pvucYyn/Maai"; - sout << "W5IQzzordTdlVKX99bpoGJrmFzYrrpUa7xxb5Hs+YvwicR8IE/2ePoZ0t04dBaIwPP3ZuPO0G3EA"; - sout << "2XGdG7f9AARr1098ES4qU/JRanR/5XZY16deNuVPRKMdUEBMKijfHoQCCorvA2HoQz/jD1nhg2ZR"; - sout << "cB19h0shhjnz/OVgb60bN4izBugSDZfyiuZvvdpnCbc7bReVBCM8456EG4mHdI1NSdGJfATpvYbT"; - sout << "lKBwrESSbmkYIVCcBxn845i5lrPHMdlt0s0qwtcqBTvjBmhRpezoF/U1BnPdETs303TMgApql/xS"; - sout << "MWS/Jme3ioI6E35BzkTXABoxIxyjdFkFTququq686oqyGCSpCxlJA2OyXwUc7XkBI4Ki35ewV3Yh"; - sout << "DbONeeMe9NAred9EhUBg+uLO3W8oI3094ZbMa7N9b0tVpewsJuqP7nfW4hZtLSpFEtjlQVGsqfQv"; - sout << "TqljECWVoZ4cbclYUXTMqtd/5okENdDEk4BJnmd5b091PoGNeSqYPf94LVrlyVSM6G9DmQm1UTFr"; - sout << "bUipf0LcSMZ+DKYhwIJLpP/GppEMORYKRhDtgPy66GNfhiNKzLP+fyPpSf6LbtCUbE3Ni+PJ8zux"; - sout << "k810i0by9lVlFUW9tNnUcSE7IPrcFs8fozpLwNSI/gTKFy0mNEnHJ4hlSgaVB157PLKmHSv6WJIe"; - sout << "6LE89xQbc/Rl2TRjh2SxZXB62FoRLXApUFcSdCl4ZRdfA5p5zXcQVWxl3RUdJbpUhsqNA7vmHT9q"; - sout << "qinoGtKuPZvANZr9Yn5hB8zzHLiRvk9NHhFdR/Ibw2gGXq6XMuJmrShSFRMZrTiIVtQeBWZNzQBL"; - sout << "jViE75H59XWkpeUM8hAUUawEH+CFcYynOsbTECPSbgIbt1w6DRB096vYoMnxnttX3J7SIf4JtpEZ"; - sout << "iSI/Xzdcu9L3QX7bQaHzaf/FIqWvdl3quN2CrSnBd/UbGCdgylgCQyBtQjcr2lP9O3QKav9laEz2"; - sout << "YJbjjXldgdumq2W/0rTopOIX3V3xCHB6xkZq1SS773LCHWAaoPRMwgfrJplzRSNIaD9fRsVQuS79"; - sout << "TUwphVg4iGrl/0u8YLEr9TmSTojfTS3M23HNJU/K8l1zOB1gfCYwmj6jGxTCuvDHvgpJ/UnViT2j"; - sout << "fnQjGXg/WUwEoaD+66ZePipeu+PlIHDSX3Jj2+jHAmMMsFX7PZlP3xhWUyG6U0hJ1guDe4HOcMWQ"; - sout << "97gJDdYzwSE+S7vB1LQWKNdEnZPmZ+rQ79gpjNYVfzE2djCO9Sn0rQD5W6O7Wd/QCV+qu619i1wN"; - sout << "IziA40bBSmDLiEtZv9Aviu7q7Au24X7ZcXxRqruz9zKPSFd6dhpWAK9lD8PCNwUrDXAvlHT8pS+U"; - sout << "8K2ISz2knDjOiHIb1P4jF374K89RNSNvefvkGBNrJ8H7JiseiuhumYfAiSVqBQwGUeh8lnPq4aeQ"; - sout << "V0gFbCj8kNEs8/cDLBnnwCGZbqRfSGgSXTKWu3/oSmn/bMralWdW35AgMM2tJ3Fo5fIkfzsq4ykT"; - sout << "ywlPUtOOHIS8ImXHCQgVt1+/OPbTpgCly1i3ssNeIkjPY5DyGk08WWYiih7IAKxjFeHSavuYKy1P"; - sout << "1QMI0dJ3y2Wp717Zg46c+iKxlg+oUR7It9UT+ePKJCm/UAayU7LyOWThMfP73hM9k5G/h+8oENw2"; - sout << "D/Kj5IkwB3iK6iC/6QrZaIHTWVRNYioSzsEndB0X9NhBMlcTmyvPJfctFydhK5JjQwjjx96k4FvS"; - sout << "P3C7zh1ww1K/x1J5vK/2NViZxgSFmIz7L3NbwUb87eXPictC8NVReqskmSw8hxNmgTHG/WEU6NZi"; - sout << "XRI+HIV5erFIeNRIixP7gp1z/o/5bL1QH5ALgBk4TRc1B9qG+fcWfJg7o0NckPbkkueM3u1J79mu"; - sout << "pcs18OQ2A8zehkFZeaTCC6Q6BRijJFBpPNnDIxJ7RDazpi3i366Hds6mD/r8IDRT8TbWqedF8bNv"; - sout << "vGRPs1aEWykMumZdtkRZXfByK14T0gF0CSjUeIRnPuhXbfMBznBUGue7eCj3jHnLynEKdtuA7CyU"; - sout << "FlXmJWrixsWkohwNSY+2jCNgmIWsUsvKS+dlNktC7OdSo+QEIDnCfPio5AwCTWBBvUaxwR90GeWW"; - sout << "yk3Rob5grT0wgxQGoCoew1J9DJT6iLEuy/jJ4FjFSB581f5186b4lOYaMoSCpOp+uWEz+I0c2G0o"; - sout << "DZKFloHMTpZyEt8LggzchgxypR0SGSFxm1X0/lhdPwa+8lKRdcXmhe2ai6H0R3lhR9ZTBaCWhMMo"; - sout << "GJ6SvT1ttMF3rg9qfFc1RKjK4+TsRxwYOP1XDz1rjEp7KWHLxZ/JKzp4IXomPm2f6exs+miIPME/"; - sout << "yo+Dtmev/tYJLd9poc+LAVkFIDqQ5ID08LgCGwAwLz7J+nLxSww2OjOxhbVfRIm0Bj0ERLmvy0jv"; - sout << "9Oz6hHcYVHPqSGN2aMsPtpWUWOgND2ofYfAJqqUMbwalMYUyaimuIU9ODyoT0xIMH3VeN/nrIHDB"; - sout << "u1J1iY/Lshk4okLUB8OnbtJY8t3KATibzVtoLaoOcmcQzOJahRLw861zYSlCs7SQpAqVEoRjYtZC"; - sout << "5JPGKLY9zqoogM41u3uy16DfVEvDYr7XZm9PRYY7H33QZiOYhPRgzZvs9nJZAJ7QGRDYiNQG5V6a"; - sout << "Zc/qLzF9UaRbUOxVKJ6FkdhuIuK8d38cN1ky2uUM2m2cFAyJmzKosMn1+ZKo85F097lSyuDqgXvF"; - sout << "hD9fu2ze0hTo2zqFmHHoDvA/3dN3Ilt5omMSSf/HQdvOeXj/2vtmJhpnERGJo0NCiOZfKI5QG4qP"; - sout << "313wIpwQPZxAvJzh9YEHdb3fv6GpwlEygu93G8Uu4WZkT7P+XXqhH5PL3cZ2MYQeYGR70nvUzJWM"; - sout << "lz7g/DvPE2aQHxGSlH+W6JEabNL1YUvAim6/uLWXvjbU+QdKDUQsml0P/auI6oHYMAgXnifheH85"; - sout << "xyT4TLqA33Bc0SRpwPV6lIigxQg8OEX1czyeYInQ7Y+YhaPF9Dh+YR2Kf6xLS/qnhOJrAr102gf1"; - sout << "apYuPnSogxRcir2pFtltl7k7xjaAcfcOUfM10VkYOoq92X9XTfbEBbeEAhFtw5AgWJw/iryapVb0"; - sout << "nNPvXwZ6g8e0LM8cky8/CR+68D7KCkxTQ+C3cq6Fco2niuPO1/IbF4nnpLB7vDS7qjrgqU4/t0bq"; - sout << "qMPMFgX4MhBikxwzWh6xnQgylA47Rbzy0EKMD1j4NLSkySg7zpm29sTTvEh68OnSBaaiNm7yoIpv"; - sout << "41yj7kewoFyIY6wqbc6bxfAc+cKLdxYIxwwXRknJQuZW2mx4wpsjXYqEwZ6OCDldMgYkQWuLd1r0"; - sout << "LNY80XRGlKZw4bV3eBiEAo8E7yU6VBqi/C+fTWCdaD3kjC1mu7V9XX2tMg4G2kaSqc6JMjOZ9VF1"; - sout << "06FJujVmJxzZ8t4Z8yI3JGUpmWgYnvY3baanLpy54Fd7M2JgoXJrElrfsEMFsci44CmK54NpntkB"; - sout << "wFoMnSxymuW02SsBU3DEBbZJmXspP2yqCUVHyqpp2Wez21u9T2LV2yNLpKHm8uNRf++WJ49zGBDY"; - sout << "vJ2xirDEOgRWCQ/z4J7fg6AwMrG0PhqX5Rw1NStM8rPEWqKd9+6KKTL3lMj0rvvVjUCylKvev2Vw"; - sout << "aWeVNhBWgelJ9v/Kdp8lQRsSyqJ7JP9CQ9iRicBTKpKio0NTaYn8LEo0yH5B8HVycOVJmPjjYCg6"; - sout << "eiZ6Vxr0laxchTlNPwRAU9hsZky0eO68C/cLrfgqB2IxJeZH0z1XWmsA5dGcYAWMDamqBNPTm1jW"; - sout << "USE/0iR1KPxvIemOxt2V565of+jareTk/Q77E+jkopN7K2AmKOGouPeRUGEmfrtZQY2sarl0tF2r"; - sout << "f3TvsoqK47umT9s3R208qwO7nC1TQjaEdP2KIfcPxbyuZ38d+GxQcyuID5+qS3IQR4X1k+lyUxY9"; - sout << "wtfNYYbmsXnqAXnM/mKlfGlYalVxalvxo9eJdugBIVbaQ3JmEovseS8IV0dZz9ItdrW3HtHquix1"; - sout << "xKf4VBhFqpCdVXBY0/mMlcWws6nItmS20yCf8i6jJ1HcaHGuPQvhyiv+xL8kELcu2avfpf93i4m5"; - sout << "KaJb4xkjSXNAcBerhBlZ+DO58TBlEZSI8UTw1+yuzymxOyNbq2o5rwqAqMDxmfJlJvbum5OnTWCc"; - sout << "2AgSoTUVOUmg1br3xDxH5xX81Ty+fMaizY/8P+dFg6MGikZZuUky2R/nJRilgi4thJV2q9iCm3sX"; - sout << "KVW0vt0AHRnUb+QkaLl4ZJwGUcx37S2YcsFCAk/GG9F3g1zo3zUNgbl7WelqICfme9vwcl+LDnvK"; - sout << "q8jgEUNwGe4ZcgDuPAxiOmIo0W0iRDbt97R56EnRs9n0E4W+/DWBvHY09nBYtVrQWpodvZ3ZkEp7"; - sout << "9zg0d0rwaLtb21iZPH1swSnf3hTYC2sxq8ppPJKENrTOYj6qiaye/kqg/u+rZmhd/LwNckkjFy4H"; - sout << "9OA+Te/sjSoRCoSSGXtGSexAhh9WfFMnivoJ+9kAIGRUCJPQCx+NPQ5s1+d5sMZHyGs6CsR2Hpjf"; - sout << "ZvXMpbZVEV/4Fgf7VMtSexPTV27hFxD0wHrhU4lsQp0T3QcI20VYx9kTz025uiMWUD29Fm3b7peD"; - sout << "K2bXo7y3G3k4ykOPrF4kR0VSkjB1NbhXftQImcpZiSDce0MZOZZvQ3lH0pZ7LaU5d++Dyp8xsxLt"; - sout << "4toEQgoyLaS22tEe1jSQsT7v4AWwjMBAQngr8MsqmcWyu1UYshp3kmosoxHZ5N0w+RN6P8dnRWuR"; - sout << "N3gIp6dBvllTNVG1Nf1zJ1r0w8KITbzsS4bBLijKlUilL4b/P8N81fEUDaXCrcdcL9b0fzv64vg4"; - sout << "9ZXsKIfw3rjw+/ZiDt6a4WtwCZvCyI7cRRB2YGxaeynx/Yyqq9xwnWThHA0a/SAwkokg/zbxfi3u"; - sout << "kzPOBau+RpDKYDL6DBkYaxMeTnbF9dRZnckTEhu8kcSh5uHhVNSeyYSngJ0YXZV+tEzNi8yI0wA+"; - sout << "6M9OX5gcHv1pbem3Fyx/3Rqu/LbjD19TtdwuyhXnnGcCXPGzqFrpEdN6504IdM7WDRrkdjpyTlOR"; - sout << "5hzk1GymO/IPAk8uRPEHeU+873xDLoxeuNseeNDRuNwFa5FcIKmIAEGeSydVPv2uPH6l0VQezasr"; - sout << "XMXlWOCOFo9eGGJW0p4tz4qX35tClsU+0ml1uaRR/fFeJyJ7+WrqyqDdHpTqnGWp9OdyyCiowxFD"; - sout << "T7MJ/Py4+ytZ++sArWxfC5KATdztRSzuquLbzKfLtF5x3vihfHzonsdHzZNUTi6py1JV0uQO6by/"; - sout << "1HjNlxiNqfOEaQxfLe/KwZvvvSVasZQz+64BSVLPlmwFnha6iUXYl0m8pNQ/E3QlysaeDvf6j6nC"; - sout << "By2Ft84JgN/2Gy/XJuQRobhPiDh2Fro4ebBwOjIIfgQx9Q9FNzKRDHAS0/HXB+P41/CzXWr2eTu2"; - sout << "PXZ0O8TshRiD5gH7Su23qpwB7qlNrPbULE06oc8ftAJtuKAGDPFXbom3zFVjsj/yeTmS2HvU/b80"; - sout << "8FCgSSxx8cvIqfPHLLOSPBUxJAVyCFPvDIUzHE3sBPyw9OY40bgSyaYoC+hoKuf0lHgt20r1Tz5A"; - sout << "Em0sAZfnbWRESfaM8lafrA+OjaSDQQW7wAI4+XO+HbWYxz6Zj3hu4qfmID2Iz2kuo57Ci1RLOUzl"; - sout << "sWcTkVqUrdnoGzjtese/OnbbsCUQIfCM5ygX1KKgmlb331Er2So/dqrYpCFAqBo3SmctlCtmERuC"; - sout << "hkB59TSoGqV6ePxidHGWKBsEHudcQyRv+pwNhnRkW+l1XmpzMmdQqeBjqcw0RAVJgDmHS1SMrpPE"; - sout << "GZmNu+LmuN8xdGV1tbylOTC4TVND5NOWaPDE+vx0YaRi2orc7KgrLc9OhtAQdTC7hlk/8jlmYxFp"; - sout << "xl0Fe0oW1fU0avYFk0p+dUXfYCA6HXTetcQJdo7Ai4JveZIUdZ5xW2snKTZXd/IAE1UG1o5Uz0LA"; - sout << "/y3Q2+1hBR+4cVnmqVftEf4ZQQoJlY2CN5Wltg8kWf3nsgSmu1JNtRXEV0DKODWTAGSizMJJleG7"; - sout << "ilV6GJU335MapNMUrlHYVNicy1NY8CX/PhZtbGzqNM3gfwS0KhWmKN6HS2BYwfPllMYJk4koLHWS"; - sout << "R9dpGgz8n+PDrKOcLG4U8xYocSvLUVJ6YmdbYWtWunGa0I0egn3YmUc6RTflJ6G2rRpjY/B//CY/"; - sout << "tAKUuY+4e+4/VTH/sm1z3yHSycLOorAeMjKnPpDfN5d4hyrqeHgG/PKdZ/QlMZYw0M2Kb9fpvefl"; - sout << "cqeu2IZaH1E7G/YbqnL4wRlPq6HSQcT7C4r7vJxwYGGwZ2wClOXdvvbdjAOxZITv116e6jaMQ3vN"; - sout << "NNd09s9zZlXyXDyBfdLgFebbKLjrxC+2eB16aURJkorv9jQydbdMMsZ7DnW75DppDplDyPvMC3TT"; - sout << "0o4PDSd+YBeWo6CAZcWNR+lB/ZB36tA/RIyCopqeUOL/P8cfeJSX+3k1yghKm4HeY6VaWJSALiiP"; - sout << "/0nEgGc4dbVmspZGzaORpGOTqqGnHm7TiZmOJW0PFrCVY0N0VqHcSkTOZn6PtuQKFXtFHYEtpepB"; - sout << "0d332dQEz4AxgF0qrhrviqEZpXTxG1bWE4uC3H0tDWIJhyJe16xF96vE3QN6z4P2+esYwjsfRdQp"; - sout << "3CYcxcjagfWBngiEfRsseTLhVf1KsFIXgafpK36gWlZan3O/7uyqhOYyg3W7aJ20bvZWuZZ94Gls"; - sout << "7VLJyTKS4LNKHcb9Y2PtX7nzosb7tL2hcYgGChva73FTDSuf56HcC5+KM+t/MXf+BPBr4FUsgN3M"; - sout << "1QrQiRM4dZte2DfAvSaZmWQuqtsH2uOLimHU2AAsfZav6k+7Qb4mQs6zdRPTMPcd8cUv8MWf3aOW"; - sout << "skdmBW3ausbs+iQFW1MUqr/4lwe4DUekn8nfniBCI129E9p0JLXLw9NrT+669orbCX3UOGc5UHTg"; - sout << "0SvFTMRqw7Y9mw4NMLOMCn8/mINMrmmaTvtEwLhHSOTwYjRCJ1A353ENYX3T551pA3QS1CrWfIrI"; - sout << "apV92iQiSH5A/rzOjhq3BDCU8HIXihgVCsR/w5Kcy7nCkI4pvbsIjPNivZsDhx8H+IKNTUNpSRt/"; - sout << "CPzh885kcdofM5k/nqVvvJhQzhpk18+IRuxTp5Dqa62MYZwgEgURS1lVVzWQX86wJl6/4cbIVEcb"; - sout << "1dowX6FrycYfy81NkQdF06egVOZVYCaZdW7f1W4Xn4l7PWb6pb1HRb3/0Wu8YuYjm8pSY/wA70cJ"; - sout << "G141hphqUxQeblVInhyk40zcaroWoWWGTkiHUnAp2cQgjZJRHbrfoJgF5UVmdDJrwdwiMREmR7zZ"; - sout << "uSS4xTZ85fXMH1NGcOAu3tFt97bY2uh/Nzk+XucsND69TUp4s7RBmiahbRw8XQa5izZMRPgYkVss"; - sout << "A4ggkTd9wiuotgjMtjd+NsitkGhuoQiQHLW0R0PVPEfG5mtdlp6/PlqCsTum8bRuxivsPkCcjEB5"; - sout << "9gdgEeSfSOSRTe0hnm/gDQXs4BnFeyG/YzHm1pHHIP2VHkr3GlWVu0w7tLPXFLXF8kRESZgo1Np5"; - sout << "WFYh5OmLS+J3xhJMfzNjUJ0bzfN5BnlQMDT8vttfAovg4IO7zFTexfLBxgKLWJZ2aQnoPeoBIJFk"; - sout << "uPBxTjUY29133ygl2fAylnqP9zIipFbgkLEw0NrfKrfKIPK2Kf9VI1+adXQlsHo0vsI7l/uWbW5n"; - sout << "XMP598ROkdbP5aSkpXPdld++HGzYtfD76teXubxsFcCpmm3OSbcLo2vAOMDQdpfFTOLM0ig1VhC6"; - sout << "tCC32GUIWix0VLb2dZCdidXJXu40BNL+ASvXUAipVBD+qBL0uSHZXjns8B6XFD88zrW+reKH9ZO/"; - sout << "gRZI+FH7eqxkZlu5ChFHJeZVSK1MwHk1ikQxAe8jVXTzzVAMcK3EVGvH/l0RKX/SJvU58t0SuHOr"; - sout << "HVule85Z1pwxt5DWS5IYXibDgxOWfrcKp1iTXhhKsmOmqLA4M8iGbtJ59RjGp3ndoc8k5lgx0YXO"; - sout << "CZKSNjMhF/swTPJF3fvVwzBU9vvWe2HCUTejmLi+bivqW3n+qtHiI1y9VWPY9B90N9OalQHJD2OB"; - sout << "myHYFjs7AVdwFYoQZIBFtaKpozISrGZBa47RfYaS3zH2aNzYnT6zDsgKJYdA8skR/tNgMQPdgEtD"; - sout << "d/NZPBXg0YnjIJNX9z3kcvXuwNnpv/ZafofCuHJQDRlWe2hvfHXZJf22IWrkeFjTGAzVl7bGry9d"; - sout << "zaaFJKt73ogJ4IM36xHJ4pgGuP+daAnif+2sdHoEavTV6zvkV8T2UXgdP5LmPVnoz0zue+DBTt9q"; - sout << "Lco7T8RhIgRDQL3K/PB23bc/gYdGKh2ISHY1klB2pUWu30sBa+g1ap7cTFIC5eDQ0L19jaJNlLWf"; - sout << "MQR0Tshn9dRsznAfmjIm4RvhMvf97Wer3t9CjFOOaQfSXxdPQ0KpnxeyfyuBpRYScUX2cCEkIXjA"; - sout << "IiOHn7aTJ22O3S7vZ2a4jVygPosfhvd/nNfWhAukM9UfVIFi/kG8CXvuPjKhn77k1ddfIbsp7Q2L"; - sout << "Na7xoC6HiPus1PI5kd/p1NkclRSCiMBCl2qQfHqVIioK25jAjHl8SxsiPpkGDgiBJTO+HR39syAt"; - sout << "iyjvIv5KH/yAzl+w13S68pDbAGZ7U8hDI/NKlhgVycVmkfKZzGB/9wzJVemHhvosb5tY/P9ZHqgB"; - sout << "HuIUgdxusvm5IyzBBl0DbaMxwdHevHQgJ7NmxtvGDDofkHQ8JQXkr+N7+Oswa8KAEdJc2t9fST82"; - sout << "X2NPa6s0wEQ9PvZhUQd45UcFtXCBbkCoisIEA4X5Af1xG7gqwo/PqEW3g0hYwmbWDgNTnEHaHSsI"; - sout << "4k18Jcem/+3cNZx6zFKEZ6u6/JlvGJsNjvoZQlRluwF64lvxtmvxwD7ak+UmFc9Ll+cYhiHhdy7h"; - sout << "e2RAF5HLTVf4BZkUs+NI4h4c5z+pU4dxM4JOVNQ5OoBisyMeFbFDlBd2x6wRMtiREIF4h/yDm5/g"; - sout << "QIQN6sXCt4zrBLoPvFBvC7UvVPaLEpW4Ay7yeYoFhTUcZ7G11HdyJHIrWhXj8mazBuT33wiXPXD0"; - sout << "fgDO2G5kVrYv837OEagI68UWoYwHFVQyKy7rXAqnJOH6rUOiSeXwrAs0VXCOo/iWz0+H7Vgkjvo3"; - sout << "a+RGXFU0PakfqhrS4elT76gTx27rIG2VuKjWnNWi91g2MnD9dwS4xGNu0nB/HiOTDKKfzQ3uoE/D"; - sout << "oP/Fn1wFPtBDC6935HgjJA30RBpTT4lZtqaVhy4F/pFfqFBL/RUU0jmlG8WvHSF28NkXMJFxqJqr"; - sout << "FdZj/ImUTWIBtAOXIMgu8OQ9LXpd6kyh7dpy2lLG5ik8X18yJnJS6WZnRAx6nX3Egg7zi+jq6P+B"; - sout << "AGQecbLJ+ngHGqQd6fP8hOU7ujU4yyAEKX3CWnvjK3DKN+fH9enZ3VHAgBS6uy4BicQeqNJx0D2k"; - sout << "ab3+B1WPW98ieaG+G6TjyFGNFwqA7ComKR7X745Px3MW0qoQXJVK6FkV1x5zOjrI09hc3G/NqzC8"; - sout << "5hR5EA4ebicmVwYgc2rftfhZ9FY8vTslWklZHaZosv3/QsoFuoDNMA82bg0/tDUo7G47EAETe7lj"; - sout << "7MRS+dqjw9rMDlry6erLVu9vOvz5gVxhNM2+Q2VKx2f2ugkn8/VGq3ufEEoEkKtnLI0aJuli0ZFM"; - sout << "yEDEHqombS/JdKacUNhM/zevqMA+F63pABb/wTCYhNIZWZM5SRCoYiduOSQYs7eR4ex+Y9a2T2cc"; - sout << "UyRYpcFRySR4XB1Nf5GVdBmuuVqvg7wuRwCKXDkxcesKSrg7HLlUT9xaIReIHoFsZj5w3RcZvSCT"; - sout << "q+d61nOKgP5ym2c5BwxytP42yQ3SisRyykroBgipHm7QmrJnrsw0OZvAkFh/bo/mhstLDPKkoA89"; - sout << "Dg/kr5VTRG7Id11Qb6CKvSb9hq0mtnGAGnYa7XTmmojYhVXeFF1aj7E94bUVjtl1rJHlpNSBRl8U"; - sout << "LffnXik6Ju9PK+gw/WXTgcyOM0Gmw91IqJsBiNVmLMB8VPpunL+J27aoPVdSxIpR1H5PjWhWyqNz"; - sout << "jDTlxcIKpM0PBs7PyNJ3a0QSQYQrGDYI9VkTQJpxwYhPOAWdIjTKDBfx/wl5UyxbcAzD2e4mXkGq"; - sout << "535EjhmfhG0SRvXP4heNN4Vzl7KMDjO9VNMB4/L7500528rL2b1wC81igCwNWpUL7Pn8/XTK8jRz"; - sout << "KCcb92liptbbjGVb1V9hh6o2aR5l6ZQtS2MHlkZG5rucr+NT/SFYoVKB93lLnq2CbGPlzZZdPHLv"; - sout << "+RG2+80boVipNp8XD53hLWtOvWMQGWYwVWSqQ6jrO24yXdRjZCn+F7mn9mP/8U2dEi546WLWvaOi"; - sout << "RzQJdz+5IcL8RBPWCcUqR2VSyPIT5Fzw4D2+W6G3cvc7mWgiEjT9Vzjpeo5D5rWM3pAPl/kI+gMg"; - sout << "42h/H45O4HY7CTb1Isa5GyX4L9vPfLXyfqU0N/8v8/UvQyB8TyKaDMedoiA7+hdWtXRMJ3cFxepR"; - sout << "WqHTCK3o0Lhh29qPjMh7hKtC4xNVL5/Cz9KmIu8ZYAC4aWXX0g7wzBb9o/Xb6mJ+ZEDmtI25DiFy"; - sout << "McNqoVFxoxYhTrUYNOaAqJYVTOauV8N3Mv9WiR+s00mjLAIEOrcExjt6WrCUVAI6XCqsWPibNFTM"; - sout << "tSBPtg/w6jDsX9H13vPzwv2Wt1gGzejC2nuqaEVXlr1UKExiXjQGAJyiw13PjRHSOD5zUWOhkPBr"; - sout << "h7lzK/HcCTA6S8PzM3yIPWYUtlzfFT1a1gIoBvO6dx3EbGGRYcePQ+xCln/07SFsUXfbKu43/RIV"; - sout << "LUXeP19XnLIFDk9NRlK577rEfl3rZN2TkeFXMESPA2E3WtUXLw/rQxAi+E9n4pHJ1dfLJDrXcuH4"; - sout << "iY3QbOnJj/ay/dY0eDCjoR8XBlbvqDCvuKDZd/SPNLseIwXXTSFxnRBONagPDgQTwM6s0bqn99DJ"; - sout << "CEVWRIrkCH9o44FsWgF/gZEcEwFbZifnkjRSbiYPRn+euaJrvU8t+x86gbdywBJg/NdAPdn7gmyR"; - sout << "V0mxYX035zo6xzjvYlOVP8cDewPK76P5sFFM/00tE134eK0jCmw2uMJ+AX2mEm3RhtmUdShzk8Y1"; - sout << "lVbWyEn8VGdiZmsaB9SbeWAX4vj763YnH8pogS5AfRtR7+Z/B8dB7wBowpfs0Kvtm6LzPAqpyUYI"; - sout << "OqvwEK1/f+fwq70lm3K0jFwda0ycYlBWhmGhpuFWYh60ictb+b19JLmdT59NrIme9aHqB8+SrEnN"; - sout << "C5eh/Ee6eAU7fcsai415NdkTphEqSj/0IJNNqpoAm6WYXyNcFeXDma7VCa/6mXZFTHi18+qWqdYa"; - sout << "FjFmYunmwGz5eEXqWwZrN0RQlZLzGTjAkNuVnrDhUqfPtTQ3Jg9Q/g514VBc/e1f4tcmY4lBDeGZ"; - sout << "yAfWnvwG76t3EfpMmnfgTyHXf+cf2g6OUsha7eTpLNh6DUT6TBPtMElNMOWMDlCRrNUAvUGaiDCk"; - sout << "dkZ0LcNkPZmrS9YTvzVVXU/DfKQk2Lc4kJw08UoAFRTXRPA3reX47HbrXeo7bU4PAhA6Flkzays6"; - sout << "NWx7RVOmdSvo0Jc0Ec2rzSfShigqkmjXo/CSxvmuemnMZaBJb6o+R3iVOdMpCsw5+PwCtlG9ghH+"; - sout << "egfrWDE2MA4hENDmgbONs1XrTLrEWl26avRL15OcLNfjb4Q85Db+r+8Q8ApGlnw3Sk76VrAoqY5W"; - sout << "RIsD0jH/CD5VBtJnFNU+qFtYVNp3kBNKcNTP9RDpi9BxY0qZuEcRGtfGJMIAtqrjo9STIe7RxdfE"; - sout << "wy30LUHKaHZSURLZgUYeY5h+OTOYDpTMjwm+xT9r2GyOOQSmNL+2QVXyrBq7BxtbTwDUOd+a/9SU"; - sout << "rCqZfwqmA6OrPbLHkRkQHYJmtsR9k/+C3R4bSl28PQDEf4jfiA2ztVZ3Ot3FLHooVKF5Vk8kIVsp"; - sout << "ZnbVJA52R4CLnmUyLsqqwDaUJYVfiOQ3zuqR8lDzhNDKYuTzkS2g+X9b91wF58wA+Mg/PyhUpWFs"; - sout << "iYsELBbYlTW6eZYdDsIlI2x7khrgJuralZPQpwt2+3cg4NThKyKTpCIDQee6stuiUXqh1NKE4F/k"; - sout << "U2M6WifViFrXuKqIChGe9DSraR3UfTyjDm4l3uGX6oE2MWXRyigPCfkHCqunq34357V0Bds/Q341"; - sout << "hA6uDgedbPjXqPj5MF7/bZUHvqnnwD22WkGXfzgCpGAwN+WOWFxUZL2adBjJoEC7hYwx+umcXQZa"; - sout << "H+p04+MqMvyzyfnI8d20mZQaZef/nY6vdNy5rXna6ZyFJnKR757m/OUaL9wrvO5Wlk0nzhQYnexX"; - sout << "KyOvQYBbd4Hu5tWQ518f2RtMZzm7ReM7Xr78leb7GVB0claKWD+7ptwWwFVn7eiphI0rnxmSY7Fm"; - sout << "BABTROBL//vZp3c2VqmpdZbhgeSyWEvyZ8KUheldonXlpbm0OahvTVNZHafDX2aUph5zYHSzT8YE"; - sout << "cvm85ivZA1XdQfLkQot0Y3H6rQKiW89VE3qNoU2IReWEYZYbTk2FHIgIEicG9LECWuoPaHMLf+tr"; - sout << "8dwW4q37Bz4IpwhsPrlBmXDdlPJnWDvq/C5AWpDkgr5JEg2E1CL2QA0YsUqLMt3ogOyqhr8jvAFI"; - sout << "mWgYRweFTHjCMSbsRHRKr/WRRljRHqIs4sV1X7wzsXXR9V0GwYnkwHWu7SekTSivJHoedUspgymz"; - sout << "Xk/OkmO3hJlvow4lyNXI/QsF4ObXSF5xvUuUtNHy4EF54IRqc1chr8E073MVyHdYbK/LYDoHJ1Kn"; - sout << "wx+A4+BWLmeVS5weTfUHronUZlRIv1YgrSzw8M3Uxf3tA9+Vi9Pzu29X2IeNt7DbiZzGzMZWkgeD"; - sout << "ye0enuQfIDcZ/AV+5B2mG6uxjLnMFp3S5HxSQEfbCwyvpxHEwImq2NR+9/J4UcxXNsqe2k92Wz8H"; - sout << "jPlROh4/lFbmzxE2kT49ro3ewacP+bvpRpWyXpuVQo/oMQ0NuljpBtIoig4csJHhp/cREvXVsp2O"; - sout << "q3nF/kxgs6BrfptKS6RE5R+gz1s9WNJ0yJk2QkurEWQReloMW/w0ktcfENcFevr/HkOtTr28gP1i"; - sout << "vMleP2QxZrdNad2iBLdpJRs9SH663kVa67RTfBczDeXTqQiFtCpI16Dr7GZWnBoYtT8d2AXtCsNQ"; - sout << "wUfRuiVc23aXnOkgD5PtRmcp7WvBDczaGtBBmTbKSy4DhlWol+B8kUcajMRGnrKDC6j1a5mYtWkK"; - sout << "XzGrqT7Bsg0Tffj61OjYU6nCDLPm2Mm2XLHmhf8Ud3WJWANjsiWQKFTNH7eZUhzL3XhK/Iet+dPk"; - sout << "Y1h1o4tGWeZPgFm9lLIhXuT/SkLR+XAGNNgrbAMGSlyTP6d2EPn5WvAL8Txt+1EaMITteGO0uKwM"; - sout << "X+vpdPJC8cocJroQOethNqV+vK7brbtz7NZsdgFov5VYoLNWpwQLigIcaDxORQBC5yMh1ygeDQjD"; - sout << "exiUJVF6Y0blMVyLqBz5OwIO1ftDAxeirjE5CIwSAs/E/CslH8dTJAL6hmTJ5Ncw70pf0NvzXkJz"; - sout << "GUFSaWzRNyrpqFh7vtXVSqy/YAPOmvbU6TxUR8I/1O5afmTKkETJQP90BiuzjYs1N84IYoVt17YD"; - sout << "xPWkQWH4LxwDdA51WSGCIwE/svX3BMYH/0TB8EMdL3FwVpNMgjCVW4srZP0KShXr6oJ18/RVSe8e"; - sout << "Ls5HmsOL5vbvGarpKLvt0IXmveJx1Lr7h+9FAOG/n0HHD9S3Ug1JrgweszosdeIho7KDV0tGCEZR"; - sout << "lLtI0p0KkoNEc6Hcm7VZyMOx6d4aN12NuVL3l6DFtd4SCFwBrIgqyDIf6ZAb4xeH2RckVs/uTBQo"; - sout << "tNCZQKMaxcgc3Eee/IOjDVzFhjW4lo33FHhTrzYzvCISWREM4eDr4/ce+UZ6E00IsClZI2h2HkiO"; - sout << "3k6ydBh51dHWHf5CZPp6gU218nIU7usc+XttaZpsodclSmh+LpXGroacfKyUGTB+I7lf+yzzBrmk"; - sout << "Eo+WFBgemz3Rbcyem+zZaRS8YXKi4NlUX5lS3heqA2ogFwFDbQJB42FBCEMi3WVr33GELllqWSqw"; - sout << "FZPBsn6RrIcIwI0/ehYLjO01/iOmI2Z+YAg/DRuxYDI/eV49z/xv0fCU2Xs3vYv8q7ZH3fjwyusW"; - sout << "R7lKhO5nksDaJ1OS3bf0u237OCQYoc7dB/BkC9BHX2eo2Ou4x3pKBn5HQnldPNqA8Om/tdCgQeVx"; - sout << "izQq9KJefumUdCmn4ChiVdPs7SK1DfhBnyL2xVHck9bC4fb3MuWZYL2XgwMwQ4OOOtUY+/sX8z/U"; - sout << "O1EM/l936v45oXW1LJJYQtIhBf/rlqxqc/3l2YtHR7Sa40RknaaukggYYQJB2PJqXNBs3kI3FwIO"; - sout << "z1mMafTy12d1ROBlqBee7AF5QJ25fCM90GgxqRwuF8yrGyXwv9pbnqmLBC6tz5DjQcYWpwm6qric"; - sout << "pQu/xFI6RKtMM+tq+ETci06pbHQIcQaTvvW6iX5tEJ1iMFJaCueVEx7Q0MOgNhux+338+BEe7Gwq"; - sout << "5g8xr553nO6KTDIrcTAgkRnQIL29WN3AOoFl5JWtP4uo1/cKqKpN3JcAIW9yQTjKtVOfSvZgSRxg"; - sout << "Eir9UhU9SgMrM0/cdNLZed3+P8U6AYn5LxZDNWIlFzmxCBRh0SuJJ+yXtPkqApgvzso7d60FhUPa"; - sout << "H0LYx+zhjx7jPhABHXhu3lY5uzsMcRnndIEOx5+N27vFUwxJC+KmgOi31g6XcPIBqwaWV5YxdVf3"; - sout << "7cYOF9FYgRaCwiE0OKGADJJA6mbTYv7s4tzcPtHEyg3XK4UuT3lJjaxjsl/VLXbWW3JKKXwOuYGQ"; - sout << "w7ExwYxyfm0tbXgwlK5nctw7Xg9U5CJA09jh2RXOQku+PmwbtseSP1HbWFC0pOskZatQneNQ6jd7"; - sout << "kcYZ49MxSI5q5+rZuEOke5NytoWvPIeCFsKMUC9HqsRPivSrczNf51yX0fIuSTXts/ch5fakqj8f"; - sout << "cA3Ln8e3wASkQo6qtG3PP2tAyEOP3qj39DAZT9/a7e5m49vjCx+ui8Kq7tj5ehGuKhrteERJ02q2"; - sout << "ObOq3gYffJ4LDddCB1HQlrK1vV0ZSDctR4QZbEhD5PC0ToKsarT+FKHCDN2GngDIh7zSmEkq1YHk"; - sout << "VXzD3V406Uij54UzOXv+6riqiv139xC8jo5TntvqR7S7/CNQu7D2ix+l+WMlUbFWy3PT6pL+Al2g"; - sout << "ydAx24ZbJ3PQLivg44d64nA87Ii+eDsrJNIFt7jitB7w8tDfpJRsCyGWDOkA81Y6Us8udhe5KDUR"; - sout << "7jekzGrKuX5VfUGAQi5+Vmk9W4d6C8U9Ma/D4wHIWkU+M0ffMVEgJjETYNMDyefi1gcCwh+OMCgC"; - sout << "wbPlRBhdqaUMFlMBv7ysGeYJUQ6VoDw/TFcNtDMZV7G5mMwtsMhfJQV32hxLDhscF8nsafoeWIhU"; - sout << "9aoihwKib0H9EvT2gg3KyvAIwNM/EjpninJXaaMNGbEFJ/7gEAZ2W/jAJ/eGmkLjYvv7Hh2oAkYn"; - sout << "5UOk7TabvEFSsoCdeTjR4YjQS18zZcz3Ta7ffCgp2htZaAyJwVeHbBBL6KoJAlsgKi1yVtP8HXY/"; - sout << "lpulZcwuaKXjQJVcHS/RcgZsaam9WxNbYh+X/CBouHSoZ2/LbtimWXb0WbQE3xk2Ntq3j7FJArKl"; - sout << "gLGaykVfPzTfXFPKF6BduSPmxLCmgLgeZU2rIYBC0UXMVtEtizbU/4MF/Qo+0jjbO2BZGb/6tyea"; - sout << "PRE/tXWyjGHkcx2T0ZUYTTpPtWI4MQN2LxopbcmDkZszXvxH9MlWC1jkhv2xBnddamb3jc1d4qmP"; - sout << "YetgmnmQzoozyXiu95T4s6V+H6gNZVhle4+QmwxOe+zNzZaLhn9GBiwfLDGfBqc2usa2FvFWpqke"; - sout << "PGE7LZ5HN+A4XFJ7NrEva9PtoPUOCM3FU9gyGdzKUyyW2ilrPX6rAil7+ZF6KDAVYOmnQQCtp5Ls"; - sout << "P8t9MlqGMM5uwELErplaidbu2fNVTXvySFbZkga6AYWWV+xQfekzRYoN3LSs+Y0OuUU86RBqyOe3"; - sout << "+5F9fvPztVD8Zc/c1jfY7/3lgJMtgEO/+TDa+gDQAZoalgf/SGpvv66MuiABp8LALajU0a2k1DYT"; - sout << "otIdBHlVLj/JKD2tfHDi0FevTo3l2N2do9NEx2tyMGoLQwuJplvP40vHr3aII9Acuim4GEqh8cXT"; - sout << "XWT5KzFF5L8f+QK27m4lgotZVF1ujXVuQRT+CHFwv5LTiwHaZ/KFgVVY3gIkjCRoP0XRARMk5oIh"; - sout << "F7G9FnoQM7oV8f2b4mTKVvqMMv3uWzd4zr0Tx7GTSgL5YfcJox9my7ibHgpBpBNtej8uX6MtzHXT"; - sout << "SbBoG3lDrdZ4fhMx8d9+/oACjVThf3PPa0exxvme5s6T3GaTnZt2BgzLrSknMaRZ1Rx2/kT/1ecn"; - sout << "GALfb5irpfKmQPLdGJv8EIAB/Br50DqHGC7DfCtZ1BshNTwgY77mI9cus0SGrgmPRXYuuEUVhFHf"; - sout << "OfrUwDNX/L8+ztdvlgJ4NnE5tUnegEz0ApOyZWywtxDodl3AZoZfBV5ODhoHNsCyZCqD/HYZXHb+"; - sout << "u3ZJ2PB7wcBbxSCf3mLSo8Kknq1d87o9YAQdyyNR0s2XvQO9IakDz3HfBFugDO0LQn0ewgxAFhMs"; - sout << "O6hA4M0Q9NS3X+Dr/Roy7wn51BkJhrLvh9TSO0+VCa1EBxo3mI5vM0m+Aji9HwySVBcd9L5FcNIm"; - sout << "PTCkaKyAd3DsqBIoHFhdLhauODisNzUFC32TZiz6VxSwxFnNvyrRQUDidLEwWWcp8Kmm//Q4nq1f"; - sout << "F2Qq30IqkLC9nObGbVSjZb+n+EwqMlE8mJxqbgkh5TQ94V+zQY1ykiRa5a98cFA5MLazMniE0JXv"; - sout << "YqUtmkvvbyFjbAZ+/RPUqG3eQxyvWxBSQSC/zpbG8rJ58ZUzuPkmmXe2z3PIzFpb2BMVi7U9t8Kh"; - sout << "+ywyagKP5gLgUTqOS6Sz6p30dvtDsayhngpRtVfmOoJ+Px1iw8f+3o+JEdJGnXEcecaGIzABCOST"; - sout << "mEC/TQQ5ecV4yEwN/cN695Nu76+cSiHR+7UyWNSVV1A9WJhsaW6NJl/YY7dZDlMYWCtlB8kUCfWr"; - sout << "4bhrudo8xW5XeM1EtmmpONd8HeMN0479Y0KqMHYCnxYz+UnC7XZBJkweyFPa6TroZoqjaIZMEyRm"; - sout << "YizuyvyaX1eYGM3Q58/qti2hIgrRovGl8VYnJWJbaU41OB2CY+R/TsuQo4EcqfvAUCgNxZwwmvkG"; - sout << "brIAJ5+fOVfI4Fa6swJ2PulbJBXXS+fCrjCEvBAqg2j4GxupCDk92IF6cUH0dgmx1TBFgRTNDfzf"; - sout << "ENcymno2Q8HKz6uvMenEfNDAsmV+giNq1jIY6gm09kMwtDk2hs1c0hqFuno7pOrkS1RFbd8onyGC"; - sout << "qoN7GtGpMk6q82mLqrEc4chaIOF5UpPSH3sRa8tbiP44q8ItnbXnMlzox3ZhZqJN6QgrNWnQa3wD"; - sout << "4qp2gUxGkFE4i242i/9xzvfYu8h461f7e3Es8rW0sB5PZ+FQ8SImlpE5vh+Q1qhXoNof+I0fGGb7"; - sout << "fbTZmz4ZZrhCS6KgP6HBmbohfPXCKubfc8Q+F9PCaAN31Fp9ycTV8FAfXySbQzdShgnnhnddJl/T"; - sout << "Vhx2PFPythxz7GEv62hwjLuZdxZq3nCykVHZ/yTE5yoLBaYUvsVVZn9rv1YD5+oSYeJZTbjR6Hif"; - sout << "t7geARjzf6oe85YZdQvODK9SMHYo/QwdcPwwnSf4Dtmvvg5XnSG41s16X0n7o7g5ZgFr+HTLwMzR"; - sout << "k1Zk3offnOMpkcpEXvM8OhFvFiXoN91CoCvin7f2gX7IWrNv0mSj92fbKzQ9Qa7N5cH1pldFuDcA"; - sout << "kQ9wHFmz+rrnzgEl03Br7NqKsB6koxE5YEzHkNLHmg+pTOyu+yDREbopMHBi+jTUReVS+5fKFMLY"; - sout << "LEB6T96s1i+ygPVSt/4H/DQsU+0caW2dCfrm0onr04auJcU+oBYjbR3OlM+6SD9UaUjt2PtNEQ8j"; - sout << "fZocgKhSxt855hYm0y5qvfBLoDaADhziF6Exuh7YM/G6ywKMi04Ab7qecuut0c2bWHXYrQPhmmCE"; - sout << "NXCiCX64ZIGfsFR1pf8eCChyiA/GW+rQvxv23bl1RxbDFk2ZO9o5581NjEPjQKJ8AyE57W0bZjol"; - sout << "8s4S1ZNwMF46UgZEJwCPLuqJEJvZyLBwahHwC1B7dgoDdZ4hjaJdH+jYEZusfsvV8ZC7hS5e8V6U"; - sout << "rkOARJZwmYES8JJlVtfbfpCqBXZ0gTPuBKt2yYIlGTqGwGu+biiNMVt3spu2Ov6MlVoxY37UZax8"; - sout << "B0Bl1cbrqIBC3LN3uWsmnR99chFqEmhi1cRQR7+3DtD9gpkt1Uo0STq0YCgbVmr4azLoJ3kcEdRa"; - sout << "cfZ4LQ+7zlFTAqOhh/kBPsL51ZLQ83ipFUgz+c1f71aM0eCFharOyhIPy6YAAPHpm0FdMv9Q53V2"; - sout << "p7flk/h/s2eqe6M2jbtud3bxxUdercWJvaioQZWMkNSXcP4jKaRN88np7Xc44pjUnPgO+AYc3Kl0"; - sout << "2DXf19IXDjAegihkFzqvAKV4zttcsT5gi8wN3M+zdoG1TcnG5gJfXumw+Bu5giUudQ9P0mUZPc4t"; - sout << "vMWo3r7ajnqeiUaN3PZaItRimu7vzun5+qOEIT5nxHNDuwKVN8ZTigvOrFRUxYkyzoUzUsOPUdla"; - sout << "nE3MUbT1bj2cs6ih5jqtbqgutSNQSGupC8Xve/W8tiQcrfRPBMdtvJIp6Iz+Z7g/SXpunCoFaBSa"; - sout << "mAuCHeTbUrCQ5xbQ5k8G5qIYRglHRADsWO/oM+vWtc1jEEBZ9UwLdFZxIdj1ytNnTwIZWstliRQw"; - sout << "hwNPyxfJQwng2WNJFG9LPvzxxnpCwYG4Fk75wPd05j14gLMcERPyWpwYurfqeQG673BTCuBeF/DV"; - sout << "zZHI0nIV9CLhSSbuo46Vkb1+FaZwwpsD5sBg4+EwKcglHUzrqiskq83hIryVcvSKRNdb2R8VDLCa"; - sout << "pPblCQx811bg68OJ9UHqbw8FPcSu8M5zbahisVzxszdjmyZ7fqWR1twCBsJu+kLjNEsgC1cmC1ca"; - sout << "OLyaDYs0sad92UjkYqqBQijASoQgafhSPuJ7sEoXvLXrPP9GWb1DV6S6mD1ZBRicwlo7pWb6LREQ"; - sout << "HFXvVkX4/seCn3O76cNuqenVr3EtIOhKC+lKDCBbnKZ2ggyoIqgO5BIteeFcs5rz4yuQRM+lB7gT"; - sout << "Nvdwa7iYLDOsYOrtBpNMD5KO95LQw6m/Okd9RKQ9yKTcV3jQFGfAlYcvXt3g7tfz7Zyj8ED/thPO"; - sout << "pcrsgQE1E99T07ozCCDNRaPc+DhgHyyIEHNNxmgGUWziRBKqhsnei3nPCg123K00ifq005MgO5wF"; - sout << "oHUXDMjbWBF+ELX8MWGaMh+a5OWq50xs6wPw1WhPug4vnEJa1j1rJxeGRSa3SsELXmeY2QoAX/9b"; - sout << "S8HeDJq2O9OdVy8bMKdfxxvxL+Q/5m35EGdExoccnU+LcpGc1fIKqLfJn5oKPS6BlNKZnMpkyV+8"; - sout << "z6VQA7zKvYP/Coj8NSmZkBJm4SoUkTBt0hZ9KEXEAAzR7Xf7BzR710lXcFdRCYWxz4KEjsu8z1e6"; - sout << "NcjhBpcgvFTnowMThMLtaWotUJ4KnGU2ys5wMNh9E94+pZNkEQ8xShAEt9le6/gSLLuXpssWJIZE"; - sout << "JR6PlTgQtFoSwYfx4+/iqbMVtbH7f6tBpLbnrIzQXRUY07Uw13p7kblM28k91GxRDqbu0FR3cWnI"; - sout << "nTZBplbiYcOM/SlFJFfThLbgAtTO0RWgJkSr0n0n4duBka7ZzfUiMXjyAHvBAQTGkHIb+YidnC0w"; - sout << "eY86YsFjo3jTqJpLYuciQLJ/ZUI1g9v681JJVICEw4CxgguvGJJNOgQDh2CEZI3AGLlbm81ftzRs"; - sout << "MpKPE/RoI5kzoo43tcr3WCUImlVdrNak6K4gaNXH4J8aui6MN3kmZ0LWS0l/7Of/bVs56dEOozSm"; - sout << "PcGfVLmvLExSbXU90kmknOftcUcTG6J9r1S6PR6qaF2w/G1t3wx4mdberrM9hOIMoXQhXEFShNHn"; - sout << "uZ/DLE6t4Dl1pvD707gtoY6UpHpv9oV5xVVPVnq7smExwf3gQsuiHnDDGrPlz+DmLnkZ1XYCaKSf"; - sout << "EQleJCCuZApUfEl9F0EjFjSDBgPoPpwQ4mhDXFjhYjYhdWmOfANCDsfXm2PUoBm4JJNI5tV6fa1+"; - sout << "S+yaFaIw3TiqWvaOGIA0GyicfP4SaHodizLUVo+yfYJhycZrWqclh3OkuT2SVndIDLs+8Xc2oSBY"; - sout << "uJr8P3Rm+Du02X7X3D94XvNI+tl22SU9CMMfG5E2kReYpkvtVsejWaeg7QKil8gyRLtkH1kIb4Bl"; - sout << "Nu1oeg25ufTr+pyX5nqCNqeStAsIMH6ynVRecIVr0R+AF0+winGiFVVoeedIQzWc2pe1nD1F8LwD"; - sout << "H1NTNdcm8ukf6AA3O4pl1uvLPIZIwC3Qk+AW3u/Xwhjq6AxEpkCGoLrUztB4Xj+uzoBpQ2ka3Lu8"; - sout << "K5XooiNcanVGjWC+0/IBmB9gioabhNXit99vFEdTQCsZzlDY8D9le1IZ5lXIGgSZR5LMPOAzSaTA"; - sout << "b7vXFps/tixvj3n9wxtOTAMREIVaGkJgunwSNST9lQ3tcveAJWnowLztcn861ystgFGuDgW65xgF"; - sout << "Fo1EHK/Sq38jEw9frNatPCJK3eIJRih53VdvB9A+viAX9IoSK+KH70Jo25Bcrv1c+6Gr2H/Rhv4o"; - sout << "quxm7d9XpPAA5dGZb20fFloxerWOvWO+QHghXZjyD81o/1hhR6ZkOiK0trPkvDLcLOfPl++YIjPP"; - sout << "vI1hZ7uTmn6qlN20FCN6P5H2YVJBnhSZrKx8gFkULKgXdT5SpVSR194vK5cwoOUY0V5b4EdSAkWa"; - sout << "3cQm6ibiokDAktBSDS8vpitwP0sLI5K/7QfLuXjihWXONCSf+RobFWqWPXzYW51q43+MP7xb0gbM"; - sout << "39I93rn8s5XNNwVdO/IXzd/HmMHiElOr6xEHMzCUErUuQQQ59NbJ/N/iVuv2c4JZ7ppFWcz1ZQRz"; - sout << "dgtOzq49kBMIxksFoFuEWuRV8LEqBbNBQkW2zFNNWpV59mqwpikzNsraFEvUPBUJKz5JNSuXVLF5"; - sout << "vq+hYqVmhd3UC5RU50BWbQgMgRm1Bw98ScOPxfMMqBFPiMntTDcd2UuFptT066sbXg8slg/itSSO"; - sout << "/CQmixHP0Km3Lz7K5OqVdIr8GYAeP1M3pjxZCEsaLGF8YGrPnIjMyjyFxFhyteYsgU6330hc0TGN"; - sout << "LKJw1F8lbEyGt5lujwnumUFn4Wdg+gtd91ewchV+uCkT8Atrrd5gvCc2AEWxRceQHrUTuW+u6UCf"; - sout << "LZvF/BC/8PJszVQsoy6dHruw4E7UwlX+TCUvP3G/wMeDf9SBsgnn/KsRr0aQmzuIJpWNNULXGyXZ"; - sout << "0OHSxs3fJYq/X1FB2EgjJMXrAczU6aKHAj1AbCUbBIaJcB2umY4YAQqt4x2q/iOJDbJ+k3K28+Ry"; - sout << "vW5w7JdiinwwjNpQkF5OO+4bQ+rqPzJare1PT1eBn5OxRpVlZYpn6AZIt6/La8Ir2iWtAdUu9Krm"; - sout << "9F4qrhoykAlcXnI2GZi+U3I5ftBgWfDVodX8hxzyS5Soo2LFCwmG4aYayxu+RigV4YRyuXdHe3vD"; - sout << "L6Vs2qmyKQ4JE+IbtJQq9jkC2PrFMwfP3nOstccqgnlGMrKMmN/UX2R0emMXsImuMGTn+rKctWJn"; - sout << "sXIysVIEupHUUD4qC1RuZGvdGk/LsV7mQO4SkPJWD1JjUADB+8Cw2nkMVCxn8gFTf1JU+ovxgHl6"; - sout << "nMrlYnN46eRJSFIFvZP0S0gZf18aJaxkgZt/YPxm7m8ZT6NLqLE54+st+e+sWHlP+9VeqA5u4aLh"; - sout << "XEJGaJozsDZxkNfKN9lGMkacM7LZzR3lhjX1o7cjZ9D5l4m40M7DS8sbMg0/5wXn3WxlPimZFLvC"; - sout << "IBLsEAoUM+oWB1xRq58SAPIAgic3jETi8vYhDR3vlN8ubWsXyTqOJKIXJ8O9Tz+aKkgLq1P/1y21"; - sout << "Py1VM0VW3XRnnzs1mN1NLO0A9Ot7NMluCUEJekVYi3jrhhLRG/tGYkLUiYcUq3ozoUZjBI3nX+g3"; - sout << "4d9uDIXJT7GH+ovsh1q5Rw7H9bijVF/qT2TNJH0DoI25kuplXnihc1+PAMp9XseXwA3lPZhxMVLU"; - sout << "ftPlCMbdP/LUPrfoq2MaAQ9oKokaK99YSEumcKzgcNyx6wUrB7ILTO3zqFDPsxTO6pxaPgOXC+yM"; - sout << "mLm+LXoEFMsByb+F8xXsrtCFDwNBR8XU92UgjMQLbvymvFE/fEeYMwOd5VOPcPeOwPs8Jwn6K1TK"; - sout << "Gl/QNu8NQOgVK9rIISGbRdpmcLRW7JY4sp84q8CHBaZ+uVRBYzdFDw8VokNyWestrK06wQG7UOMa"; - sout << "AZGqcttVsN0aePOk13IfTG1llEL2fczoNioOpCaVqzzUSU4wA0QBOa33h1VHOY2Py3x7PWI2QIJt"; - sout << "v1IJSEmnpKgVzYjSnpt7kUlB2dYv7w3qwDeiE47NuK4ywMZAUuONlL/ZMpR3+I/EIu3VJRo0RknW"; - sout << "jKkYUjOdo0FrGhQWjuISkZB8prJxAFrTDxI4aiHSnfMB0q+L2LJU8LMLLI3Ok2NIxWmdIJaFKckH"; - sout << "f3JUFpn784RDpdHlIUU5EOzfqal9flGrMlz03gMrvUiq+9ZtJR44EXoUwLLdAWN+FO8a9Rrj3PUw"; - sout << "E9q2KZ9M6BqLc/YdZFHlpZYOiIlE5x34OqkVqcQOmlWmPVP4bSwbXwmk6oWjruRxmaA2YQ/sLIHm"; - sout << "XBZZQdMe+i8TqEjte84I5EebJRi0oiAzTFUlf89NWonCzSnzdFQPOiB6Ik6dX8o4607SkyijR81F"; - sout << "5ZiusAkWNx6zksMfeZzCxnS93TqiOSmFVrbxMx6BkArmc1IWPCFu0OAQYC2af0KciOEndo00R0xf"; - sout << "wQAtJyKyfM68kaHebkMhRWDwk9HEwznfn52y6evH3n7NnDttgw8PnphD4/l19wWJdZ2l4tcmY4eX"; - sout << "ly2zjWCcaIsx4QMz1gEGc7lvMq0J6RbzsoH4oa6Zdu6kaCN0Fy246vkDDBaW4n/fZt+RvT9YbiqO"; - sout << "fzMs1dcXd9mCOz2yB0/i+XzzG7awbdiR7bafVk+7/8H4y3qVyfGrhilzYSP7qEjF0ossijkvU1bo"; - sout << "OblpwSw4RLL1npCLQDtXh5ugQGcsEesLEBq2uFpxPTSLKWgqTr3P/XL+EwNJgkWDIXdMgG0Ft52X"; - sout << "bNQpevk0mqynVmh3HiVnAZ/mUaRXZG40kqcfQzxtH1gvf41S71sDXT+EOQDdwxygNV9BhOfyA4eO"; - sout << "gNeFlhuCPE3cAzKDUrE3ITYl1++4JIpP3BAcsT03GlBlSL4N1nXi8YuTCliEmN3TRF6FLJ5DkSN6"; - sout << "tCk/xGsBkaZlQXjj2P4FV/BdS//Uqh6EBRnRMoRH7LgLl0QJxJrj9RxDdRwW91fNOAJjg53IIBX/"; - sout << "XCsZr09+jke+Ci0r1NWHRhmF6qNY27CVKs3Wub6dYS/GifyrN++qpK4aTOLkkXQv5v6Zhs48qFAZ"; - sout << "4XORp6I1s7vCQ/yU6R4UeBNoRAT4naUFGc3gvmszzFb0Cp+li0RM9wgW25miE5Y1PMpne0nBvV8a"; - sout << "k1RYRJA1ZyOEQbmFxLMqanwITgwxZT7tK+nSITSmHAo7/m9IaopgYOumkPGdy/KsrjkR2gTq0XxL"; - sout << "OSjhTS/sOrZhSV0HJ4pBgjZMIAlEwv6k9XsefXyREo+rudBS9CiXW6TzIViAWCMc/hQo5zo5tUeA"; - sout << "vmTrWxkRgUmdsj6FGS21aQFPADj3oPrAXsTiH1ZcwpyzJrT42+4P/SLFw/h0NBYRozI01AVVaxQ8"; - sout << "sbJ8VUFliiZnB++Mh46aAF1PzomK1NiHC7n+llPAdEqpqiWqVH5YK7CpKdmCPnTUuOb4a7A2Ylow"; - sout << "rodSKCOX4VHwxzARShJxnQz+FIfVIzpiCFs9ZJKM6x89ybXTKVRrmwTbTAXRbihUKa9d6t1vmoDP"; - sout << "07z8WyFDoeWoSmK4XZfegZeZMIXF3hrDPCYcZEluxujpPG3ZPPw+/pGjBOx3/QFJTQdeHBz/PQ/G"; - sout << "wM7DkD3NM3fVApST0LbPQFKxlEXx9tCL3GBul7YnnUmF+N3OIs8vDoJQqNqVi5ZQnGt2uFLOYRo/"; - sout << "67XsBQv/ZwYNC88VlJykWEbbVDN3rY6i1JLr/9WRygS6aqF2JJBTgLwXpjfvaq7Ygttm3WiT/dxj"; - sout << "QtLg9L/y3Q+LoLOKZ5syIdk1+oQhYnSqUgTNEuw4dcTXj3v2lHm5oxzst2dtierbluemr//Bnclq"; - sout << "8FdWTT2JC9N/YI+CZi6K1lz1Bq2yblxL+BC9UE3rtsq81Rm8kJxSIVQ9HsS2ligaxnEfCIPNvMhV"; - sout << "orLVKxoWuOfhJt++IepP8kap2JZtLdmdZSr9/6suSkInlANS9SkwaNWB+EiTUm5cfu7s90V+m4J2"; - sout << "SEiAMESJKLywmeQi/JcV+8lEiThyYTxhm5huK2dUGPWnzKK0HsdOuqRs0RnHn8SQIgLndjyTzcV9"; - sout << "2mpVtjcwCRKVBFiPdyeqyVI1j7srk8WrRc0GozuUkSBMVk5KHQa3/qZqiV1w9KN7eMRaYiT46bP4"; - sout << "2dpdvqNrms7/qmyv0q+Bxp7JYv8bYqlQPaXgHxS78I7HqFsLvP4kZVWxmhCXIoIObX304w3KRWH3"; - sout << "WlQEj/llet0DPOhF6EPfcuMeSvH1UhucSS2pnYwo/P0BvMYvj6gcbjTowWMk3vmjiZV6a1xq0Nm3"; - sout << "sgrrS/RqYJeGPM6THw80LulT4e0aflcyFN3QQ+C01vGHjpaw0OwIW/iWAMQolVwcZznJPq+6Aya/"; - sout << "aYbiphirZC6fZtUMIXiKRrUxQDyfgI0y8XzJgf28AxgdyCP9Dashm9uelYMGU0Jglf/iwa2K1pkL"; - sout << "FTS7zfGJmz66x2+7agbLuCHd7AQhZb/rfOwauykZXpCE9ooLMLgW+BlKpqgVynLrt+b0I3KqQZy9"; - sout << "KkFxFwC9cumQJPtKGKUigDLV6+orLNwgtVmNal9RFHppuj95RWG3XgwNLLGoE+K5TiLhLY1na4AF"; - sout << "1NXsHL3pFjM2VHEU6cOqVLqhEWwGhrmBoxIGrbmT//lUiGwSLcmDHCvv3Qig43HbRmuQWW90qFzc"; - sout << "Sp7atxofyP8SqmW2aRw2SRH/nvCTTpKw4s9bhDEWCukZKo+bpfp1ti0P4DzSiUxTy9uSjWs3+7Cf"; - sout << "nWsTsoUxU+YdC6rDJYq2TAcn3Zl6YBU9XZl3YLckRqpUGUQ0IkF0Fc0bb5kQCJH8h9qc0Olnqa6s"; - sout << "sjHyjTwkzZWFmuvJPfxEZzsZZFu9qWdk/Pzx/46e3J7/VaQhtyYidoCdV+wtN9IOuxnbyIV0A+vx"; - sout << "ZAjS2f7y5aPxzc2XAmlsmNLBqrRfMm9dvxN+gYVD3UcWgzTVxfIXRyZqmdAvuHoTq7BV8FYPMIqb"; - sout << "SKpCcdEztosRjzsCxioB0JPCGxMhHgPU4FCNBy24fa5RIz3rZkR7xFdTNw4LTkJ15BRKMsBxHaiS"; - sout << "E30FSLCvFvBVlUITI7aOrvc8+2IgX5cXLxHQeqaSTyKm76ioORshYyRkp8K1Ao0hZXCW8rks9fxm"; - sout << "FeRen1ureYGmkoKJUWq92iAPwNsFvvxTm4IprFoUDn4s56Ung85NTvcEJEyc4yVyevqb7hh4axAu"; - sout << "h9N2uFPhx9YPLbOHA9m84PWgt/IPOfdwhlylIL50MZ9qitPPG02an2gjybEvicqckLYZt4cY1Utu"; - sout << "pslZJgCrFZ7ITUBixaxhkuXW1wxwkF8RGosHdfB5zycnXkvXhdV41+QDvhZ1tR2E10jwqjVR/7W+"; - sout << "suB35kUFqrlvWs/bBTQHwwPdNmGiNFi+uq6hIBQCm/J0MbCaYcPPfhtO3LIsd2KeiTz5oV8AqBVu"; - sout << "NPQav6McdWJjg6dBQw4G+raxkUf9Qf9TPQspS2Ll/Kmt8WwbqiRHutPOaxaBBeE8Igx6BT5kIASc"; - sout << "OsAnqIzudE0cclrkxN4aeTglqmwwq/eeZUSOzkv6Ge0xtpYDgqfGbe53fxtYvItYYExMgy5HdojY"; - sout << "1zDaNXsfHTebUUjPnsXitzIQQpRobTCe5ttWhfeeXOvE+MPLRt19KxSrFKHaRlQtjYsmxdg78zc3"; - sout << "8ceNlnoQbh/puSKv+QWnnELfDnmgVZnU7lIKs2xM2B+pNy4Q2YX1wmIqlC53rsTzU/Ik7Db7Z59s"; - sout << "i5QWZ0R1ll6rdkCsUCTkK8aqsF+mSvWNbMd269Nch0fSx1D1q2NN/4lHDDoOX1JjLPMxVNbdxLMt"; - sout << "gP8gpiQrFJWZRmTgYBW+biZPtiKgOztsDrZ+eolNWpsGnLTK9Xe11lZrS2fPton4L4jnr+R5AnHk"; - sout << "F6tBVhSBRHtZdF/xNcCsSOKvTOZJCtftMO6XaKMfsI/yexeLz41D5qfMVSBHY4bIgL3Jgh952tlH"; - sout << "2blbe7lcZxZN2XkUXXaVaX60XDTLgZUbGzqJo0+x/TJIAuCAlGF1XeF52jTY7X1veE63l423NPsf"; - sout << "kCmiRcepJT1ihXQDd/VU37tJfeGZrN8FjJzx53PVp70r7eLJMedMbBUJBYmOofO2F+OU4Rfyab3Q"; - sout << "Wy1ngiJXrfjF1DVqmKNfY79SQWWjlzST97ldjTICsyQdHWn8j+Bof6sL5k/3d/cT/sohhSSl0Bux"; - sout << "GssAFa27VYsJYMjnriFZD13ZYmjuesSXyQ+EkUsHn56MB2xEwLRVEhq8ygDP2eO82PnsIXh+uVFe"; - sout << "0ZQ+eGSiGZtqeL1aoePOA51s2eE4DxGBHWdSNr/7Nc43oUU0P7sV2lut9VmhgDleV+Du8IaZUMIb"; - sout << "2MS/TkvMW5S/ebAoXaL68zmsU32tk3C0FG5oxbdZD1tcZ4yHNJ38HPSx39uOfRG+FrmDqomfs3k9"; - sout << "pu6KaH/Be6Urzd7mr4EvxaXHz58hsJGVH31tOdo1jhg7i+6lCJAuJX01FWfnClLz0mSJnIrcPge4"; - sout << "zQrhghg31+fLZw4FtNmiJJcU/hJPGhUZ5LDJWFHUi04u3+dfP1pYR4e+CEOyvlK2YXx8CHcvjWJ2"; - sout << "kCW8TMN9I5arBUTXdFfOg/v/EH4cw54bmTgzjpn10HlKqfJn/mJPPbBGCCS+pdGmNIzItVME9IES"; - sout << "Gco5a+DT/a/TThfkRVKhJShe7ccCC6uQPdU10P+DV2KPxOfKvar2ww3I+VZ3FKxhC0AcMZBbN0V/"; - sout << "0tTOJVH4GxjH84c9Jr6niQJ/2w4G6tuTcJ2RLb+HnzfingM7J9VOr9C3IfG0jndHCww96Exjjk0j"; - sout << "bE+Aq4H39i5pH3jEvdMspm1B3VY7EN2s+fC5jpmZpA5kRb6Vj+CYCbyEYNicyAJqCHdJLrtHDiGN"; - sout << "fIhpfrkpc4+g5xHHel+nnJKgduqzs5eecM68vOtBTdmLa/nxUz/VBywqWNmmN0v1/g0Jlw3U+IGb"; - sout << "bZOvv2Kg0FO5rJmnRIUsokPkcVxnfz9iyf+l1d2QIBmeR+YeNtLS3XUfIjHvHwSrZ3lJdfXNnnwr"; - sout << "zZcKmHHNAoRLIhM/0ZYQXMbOvfEqUBZ0GCzMdwiEuNwVPtbDkgmcWjYR6ew4YjRZR9alIas8o+qF"; - sout << "GkL+PT4LEHfASjQPnCZIhUISIpwzuf7Ii1I2Fk/v8jBbL47msvPsQcVH8JiFiWBGCBfSB8IfSSw8"; - sout << "sgc4bo2hwHMIES5W+rXXoQMSkGFP6tUfd4GvS+8t60UyDUePQbBGj1lt3ETsinD0btygvMOqDhD6"; - sout << "H+E5gRWBXOV6QrXtQSPG4ZSPlDvATmDdgLCBtQBBMTLMg36Kvia0W+Yyu0bFtFEJre9N/bmyyvRR"; - sout << "P4tHn8uOTi3ry5PT2PQiJ9c9utByvU3ydcBhoIlcdQz0f1Rf8y0eyKj6qt1/OhnzVXuLrkqXLylC"; - sout << "sthAWqyIavUltofLnsAP1uMHZ0pHeX6R1lwpIROxtRH6p/0+//OK4lIZx+7D8aMPIaefQAB6Cf3C"; - sout << "jKnvyCpR3QEh9JLv22OghKwswHT50P/+z2XbyoQsRySXNTUbTsGJuYBazgG10fe6YHQOTrUDUp24"; - sout << "6PS/wEco60z1OF49dVlYak9zGA6kTRndkjWDu49NFhJRY8sBd7TVxeiU34NVyjIDLrLlx7DcuZ6P"; - sout << "1/XcA8czU9m2n6VDgkkAB6eaeL0XhB6XZfOrg9lY6R+xjVYo50Fg9A8AGen5T6+m0PeUr20e3WV1"; - sout << "H4KANYIvY6+zN+Fe6K37VaO+CbDZfvFMzifvzTQWQ3kDDTWX/BLfFgLGZ6QBWrTwF7MNiQ6fCpG9"; - sout << "BAYy9V+QWy4Iz9lqnp19J4Q+cqlIUDBp8b8vBNyrOjjLjAC7ezFUujvB/7RrKtnbaYmS8vYF1f9+"; - sout << "mGqA78owo1zfad8DsvGEnr5J5mC8d10rXhVXXb/udkiU5iEhYPnSxRy62tgLbKHJOvW+R28r7lpc"; - sout << "yDK1++NYKRpDIHYMuaZ13oDNdIoQD+d42Su0NsP0wECAoMmQKkWUaJyYUyFqzmQ2FJmvl6hDSbrs"; - sout << "lDipexBL2U3slCAJbX/PyvE0KrBPBe+vT/w1Z5s2GBvSoGwmFlN/oa5I4TfcTA5W4ie1rBHUKqPr"; - sout << "az/4un38eXTaF0Gfiw86pwlgJXWr+D9qvXQApo6KmaJhjKo+4/MaPw+iEQrU3IM44eaqq9exmiNF"; - sout << "/SgBv76gC8hXFrucFFp5znYrl6ISQUedvk81jnI7ce0Up1jYsh8fpfp+0V54IUNKqxT9YlgfIkbG"; - sout << "THuQQ3p2F3gLaflJWfZEo/lPtKc6RqVadizBP/oxl7q+zTriiyK1JWiJojr97xQcNpj5j1QF6uqK"; - sout << "+7NQgfVevfP9FGVKOlIgvsqc9fnqWd7pzWKEt+kfjbpMGpr0XFERPld0+EqLts1kAj/ejnMymrXY"; - sout << "LF9QJotJBy/QvCuoaxdptGBtrC+qJIuoYKTNTwW4A+kaf3RrNr4ohuLBcH8RRpHFU+3fEDW/kOx0"; - sout << "fdRlEUJoxdfK4Za4moLfsmI8pfl5rBvlh9oumT0sgvPg1P3zp70UXJI0tSHisCb/uK+Hggdk5AJC"; - sout << "gC3NM9uf3AubM+CW8rJXM6qz+xwOeTuMawJTGTrSg6bB0jREYOXfvwZ7oNgiSdqe4+s7tIhd21Hd"; - sout << "A4cJw2aRWk0ft7jp9O/6A8II+hJoxReOnEFn54/kdM5LFasSjd/wwxsdtGevYixIItCTJ36K48dU"; - sout << "nndygDo7qFWaryImEjgErq5NT9VUfg/VdIhCfAro6xE/1C5mMZ083LOjvTdxdRO2qtgKYCvL3HWO"; - sout << "Q0k2/WqcaAKyh19cHsnoVKd1gXCummMSRahTGpmK82fAE2vP7MThuNel1CGti+nKVvzu7Yf3i9eJ"; - sout << "rUapLrviTwh0cQzVAWHXxgE1PB5O1Hj1/hxwRVStuKNsPjZKFmNu+j02TT98hpYCFQ3F2hE/jsIT"; - sout << "VN7hr7k16mXUqWbCzNWO/xo86NyCS/8HYFXMomE9KRLlP1cw1KrmBltivEnesHGCi9sAyHnE9bcX"; - sout << "mWXL/tg8wP96Eh10KeJKzx80xfSPz929wa2Z+fAfjHqVgroE6AJQNKengtaLjTo9vBo2E4mFbs+O"; - sout << "AUy5hDVB7Mm/koguhT3+BxefAXoUbaUkNlIewCbDUlLIT2o7ZLYhoR1WTBv5K0eQIt1wTFytuCVq"; - sout << "Ldoni/6ad1IzqamnGBam4hFQlbQgpezUea29Idj4dhUqTmksk0fV6sXYaT7P9ELB+W/OnF6IY/d3"; - sout << "6TUUvnSPfyfOHHxts3HnXQjDqrRhPeJown/p6tzdtytVCLeUtXu2o1SgfGJrs9uAtT5+3mt/NjFb"; - sout << "Z+VX86HZQ0A5XgYWiBDvd/Lu+ptgu5kMX/QAzBZp4Ubxo/sF57ZuA3TJMx3CjUFgNBwHgdWsiMHf"; - sout << "X+sBqB17McUR4Ar0OVoRewmYlRL/J9vN3ZJ/P24Ctp/7Y2Ozk6vJZBIKnTvcapys6mah6rzYurq1"; - sout << "vQ2xWL0+hq2nsbkZ6bMl0ummFaCrPrMmBLGAQTa0Qa6QqO8sCtepBHRzJdCH8InI/jRBJKrGM2jt"; - sout << "s/bM1qvX3U8jLY+vGwt8URt+H9VrF13rKxlgTJmu15oC0duyGyc0ejeDsopX4NkOQ6xScBsPAMV3"; - sout << "ObV53ImrQQxdypGM8UWByxISLsLxMDDB49DhgBvOqfJCJ62M/m3zZNV7PzUkUcI3iB8QDpovj4Iz"; - sout << "CmTXnRgtsARcRc977luV5QiiofiyUOVQFbBY7obGei9EnfRspuXIwDsShcRIEHEz49K5SdToZ3Ky"; - sout << "XLZUEdbMsZDXWjmE1A+hN8G1oAh+fkFoXav5S1xvmYqr28vAff3UhxXH1ZKVGM0ePGoaE+AgPIuC"; - sout << "NawQwcyWFoPqLjaWg9bgf1K+gbhxH4ot5ehVVMb5YiAbMrIp03ONvVFLGWzI3tzFMMTOemMgM7UI"; - sout << "1T5rYrWbm0q0IBIhEpRCpR1Smtgf3wK4pUX5y+1+xGk3bIl+Sk2Niyl0DmrmqZlenbbts1n/hDyX"; - sout << "ZS+148JBO7FJvG6L/oD7LaIzjBAPMWB2TLARdbc/ShtzxMY8l58fxDoOs8gZXcpLPHpdgEALIJWi"; - sout << "GHRB0U1osylCUfS64GUzB/mVA4677H/n31R1WaghcDrZRgZs/aeyy8DIO8fprsM8MwamVSDoXU4y"; - sout << "YNyDupAJA6GKeYC98bCgZ64TdmXX3pDp++8TOyDJ0VDNrp6LfdqOaMNZBoG4G8KduFcUssKyGs8D"; - sout << "mGZ49omOt6rvrlZpGgBxi6afE/7tg4ac2hHd368gOLuPyLY8UKBEqhRGG4POlbg+v4AEegzLQdmx"; - sout << "GrtxnwcojpDFU3k+HPD7wQwv6dSpzCGJy3d0682y/x3muJLZ/bQaZUV5yj2SCfgSIaIZcmVW6YFE"; - sout << "dB6GXs8kxAZgfLLPT49+5jIL2XXkcmK/LK4dY3ah3zGKd/Gl+NUxbZmgvOzG2LFmPvp1Mr73WZ4L"; - sout << "q/K4XId/B35/LLnT1bboTQ0L4BaJR0r8uURHEUO2fo9HtByhvRmaDXy/+EAxT01G0sjy4Nhwd0oj"; - sout << "AZrryg3KSdCTwUoi/CMme7l7udmRxUMSg+L6+pTXgONqyBNDwkksQ5aw5YP0q02WH8pd4kVHHLkc"; - sout << "oVv3diXYApXi3VBhtVmpyjAm9xy2SPL/iaoitL7/0d9nIlPymzGh3Ko+ghijUD0Ft1CJHr2pn4QI"; - sout << "zWL0rF8DTAP2TcD1dl/I1VNW6e+GLtWgI5+/iDElGyrELfamWyu63q14Yp/Uk/PrMTsxRxZBzZ6j"; - sout << "whO3UnC70N0EWPc5M1q2HdWuUadl9tbXFj4eX5F2OABfnMgQHDo9F+pux+TxpCZcNFWHsd3frjF4"; - sout << "HEg6qkqxyaqgZLUM7e90wLxYi2/XV59BWFuZC6kVAkBCNWMZm+jisGYdf+kLWVrbZ091mYTllIpt"; - sout << "qP2NaF948T+/rfiT1TbhmAGwj3NI4dd3azqqJuobPSjj9pdK9JEKhyC2QlkZ4HgRqyKTH0xPA1Bo"; - sout << "mBCwzYhubIB2Ro/oxS152TwESUPDvZXTsDbmJXGsdCrrmfQy4NCXNQEDQrjrRY40qXKi9Cxr7wjn"; - sout << "W6NrSshxpsV1NwlSGT1Omn7RUMTpe1JaKLINxTUJCCPOuNCAgbkmPfB2L/vzOq/PJ3/EGbolcvCc"; - sout << "b9zvJpUeKfNOK9oqBZ2dZQqgGAD70uTPitKu0/5pA8I+14sLkIfVpVAiJI/54Jl7cz9lhMQ/X6oh"; - sout << "ILDtsAHQjcDXP9BCfByca38PJy1k56vOjfg6Tuc+0hPcDkhWobXqi5xhJaS8WftAOhqwiYZKsimZ"; - sout << "yFuYCz0EeT8EGFa4APSgjPSmsV7jCpOWoY0RtUYvhNMLSmFuwOVOiUHLrlsb8gFXQ9nmw/K8hxHc"; - sout << "9kP96Rx3f+y79BSrhUcQyUrCSV50Dkvq07wfcAEvz2dzSKvz6zTDIKUFxfz6ejsAvrcx/7UgP0i+"; - sout << "1rtTnLXH/Qy6rlJAkxXknkQQ6YG8egWa0mX9Wh7RxpTIBHPaUU8gnIxo5/RZEPOqrF9DtYoJMOkB"; - sout << "T6davyvC84bSPrk5QW0DMqzZtHDDuzqiRYZt9PfEvURu7iI0IJxcVWYQcXJL/ZCp2GVS8689pmYU"; - sout << "zIse1tFwwd64kJJfyBqN/vW9A4aI/PutNu14PwgPVr+NeH8wQk1L6MhOvcnJwYDWm504takuedAe"; - sout << "hB3lePRnMEBawImStuD73hcZV1KzTKFvu+ebNR8414Wj6gVJYfPvHFv333u0ROqJ4/Mhb8occomU"; - sout << "iedo9jM8ZJCMNbPF2Vxxgg0ahEwTKyq49Qht9fhwUTm6pMQKUXOWU/rD3x2BwrKQ7NZoOUqTlfGI"; - sout << "/czGxGeP9PqmLcleyMhHuC4GdlvI+ulMTlngO760teQeuF7EYqbwwuwqtSV3E/pbXm15OFGSr+Jo"; - sout << "FrOdb5/WWAQ7OZPwdgYiWxbLELUCjNu22PoVAN+PlAYShXG/qR2lO6I8Mh6TXZ2Oo1tP68lGp83c"; - sout << "zpgoFpNuaQzLCWIjC4ka5v5k+Y06crZnzCzxaPsanjgOtAlQ+BOykH38ErkDlM4M2SGs8IAZXW0R"; - sout << "zHgZygNho43FDRQHPiYxFtGV2ktVmtGNTG9YtWQsBcnd/T8xtUP8DI3tR3nCg7Q9esbsZOgbzCIL"; - sout << "bbtn92I2iiTkjuPSuuYoWPzVS+hi4dB/BQWeHtnwdNAmqU8IAJki4jNkjh/6NfTgVepkT2Nc58pT"; - sout << "V2DzfqFrhOW+whXsJIFkMSW0dXp5TCuJBbXTIiChMjXI3c+Dzes1/6CF7l2lFA9Ol+AiPwjPvhhQ"; - sout << "/9WCwWzfsmp3+w+9nLLt5CtWRePIB4LC4nugM985fxf10qYES/K0vxKR1W/Ox90s3D4aG57SHpsX"; - sout << "8frJ3HU/ouV73ZOtCg/lOfdocNCQa8KsoKhy0T7tDnWoKJvk0tT0RhFWltVQHV+sVteGXOImaxcZ"; - sout << "MXDU6mnRzp5PEkQnNnBDeynPvs0vWfVSYpzQnO1tyIM2YYwfyf4vdn82ikk/Bq7onvdohQH02/jU"; - sout << "VQjCfYj7X93of1bK2ja4vdEtcFpH34YTnSyZ7Rc+Np8BjO915a5XsUagea3ZHQbC9bWM75QKp9f3"; - sout << "HuDpEn4tNg6RTcwWf8s2G2vZCfIDkH0geal//39BsVC9LqIyEF6urWV10NerZXv8fumNmX/5JraN"; - sout << "aZ4GGdnhHPnryfHkHG62ESpmUCBO+85oDXrZcI1riSFKCYP1eBn/KCWkjMf0oV/pYljo1KrZgQay"; - sout << "6vXXMdX3Ur6UUBO8iFht7ETaE5BeQ+3CZQEe3tdz1Z87V8rI1Qe0Mkq40CwniIxvk86JigLkt1Yo"; - sout << "iQBWhcgJXCdVPSB1XpOdtZjgMPQNJqJtcbu3SDHsWQ3TWA6BDtVbHH7xY7CPVN2si2K8B8/Czxmq"; - sout << "xptTLBIMfU0xaBZTXLSmRKNTEj9D0H+PwHvzPper0tQU5NEs0tZ5h6FZER1mgFzEmbZ6PUVgc79f"; - sout << "ZnMaLmKpirOVc7HwFfLuwxAlvZRWtykLc8AvkZAp+bYGHERo3uHgdIKkeW82HQUKsUMN1VglX/YJ"; - sout << "gT3Htmy3dEuYGNh+OKulcvAoRvLmTMYgACr/0ZNWoYjqAeilI7ZoqBWHYswLrvefHlIz6WAAHwKC"; - sout << "xkbk1H0uWe9IRcf0DgEvWkXSrWRFHSS7uTePFxtLM7OQW/GmGfS3wt4/YLomS1enK1L4tH8fEPoL"; - sout << "ZeZgjNYiMg4gQLm0+FyseqNjiUnQPkoFQg1jztPOZSxxZToCSdeUIawgr/3KKQ5+d2w3LEOQNAy+"; - sout << "Vf+o5CjsfXpYJ8HVxpLsEC3xwVEzhpeY/04yUW2ygAfZqbsLARAAWtW2GcPN77MnGAmm46PeCSe/"; - sout << "2QS7gM0ygtJewBCaxSfij+Xg4HQhXiKDGuwDYqUDmLInHskRXyVuuojyCbmuNmtyj+Uh+HsdFeOU"; - sout << "fqTBHfEQzzAFVyORf14oEhGBwDuvMzLjciKSfLu6KcMCFo0TGgW3Niu7kDdbxOxg6Kwa4wiaWWk5"; - sout << "mJpG6uhNFaDqpfVLY5D/o0Hn/QfMZwR/MzmiFjstPVEd9ZzuRvKKSDOM9Ult2lTU6z6Ci2b4unu/"; - sout << "qfLIE8CjUAZrZnEFbC/KWx9I9zuUuoXS8elD9aG4if80uCyJ/tT4MXjx8iRsbvZ9B2MoFdO9gAUn"; - sout << "qafo/IYR+5BOderTedEwe6FanPX4opAcpmpXM/cbD51QA9LCLyNtSoSPlwji/ZtYA97UUV6k5c47"; - sout << "TNFB+QtUaW7/DpPZbxsU92jE19ztQ8VV5Ylm8v9RdvZDQ6T37KVEYu/z2bHX5L0r/bkEtQQ3gf/e"; - sout << "TRfpy9j5f1lVMpN/UccOtsZfQpW1WvDcbAbOjcuPuuwNOudfzztYg8vjJNn0pMATK1+p4adLcxJ6"; - sout << "uCh+zVgxAS9W+z/t5X3YTMnFDULRXqs8lsN6H/1t+gVBp1uG04BBDO8YMXZsUB8dLyb5BHbkjPiU"; - sout << "c/zt703lv2U6So90+H+6hlnWDBvfLHI1W1C/RkelOCD8WVZ0rk/sgI9UYf+jk/SkIkZgbtVTB5S5"; - sout << "gd5IXkexjN/kP372AvWgX1t+uKVmKRCrYlG8TZzDrjRD4BjS1M72OGwm3MDT6GPRcaGc+jtSGAxd"; - sout << "eW+6BPofMujBNOBsrxKejRcLsddfOrGTBqP0Wc8B3cNOjEg/ITJ3LOu3FFA5TyLh0BvOEkuiD7Yf"; - sout << "SpB8L9jDYG9nCcgFC19CA0Ji/Oc+aYFDMiDM4be+gG9QUdAxk7Yg13HdJirKRiOvJkf++ayrJBbZ"; - sout << "xs8NEcd6lN3XoogW4gChg9mwlUNehzzjIl56wG01T+29FhD8Z8ebywHq0xEbP5aVGrEzwj4HF7kz"; - sout << "erUrxlRZbmRgleNhldZDjqUpMcHyJzF+OxZ7W2AfpjJf5FaF3E4Rb9IG6vBtPhZbk3X41VpKx5Ih"; - sout << "DedeX1BVHzRYAPt6JchdpOa+F0Y4Evke5XR97NecmGoYXrSkLrArSXDu2qqeQVx6XJ2Qginw4711"; - sout << "fJwfVb8QMMRouBWEYz7k2LflgRG3c3XL1KP8npyz3yNw+6b3/K5JSq4hRFzXfsIWoLArSWgfboMa"; - sout << "w/wIVdPgwPnUh3bfYKEXsQSenrazuqhPosJRMsX8qdZ1HB9PBwMKMHqDessyUX8u4kFMZwo1egfw"; - sout << "7x8xkykzBeY8Iy0y6uOVpowXPgEPg9PZnquqW/r7qQAiqgoW/4oMU8DZtcfT9fTEN8BI0YPPNPFY"; - sout << "1N02ocPS4kd5V8/bcTWkYmtCWIqlDtyPCEcEoBQbhneOdw6EmOmTqJVArAwydyN8TLsJq5A4WOf5"; - sout << "wWO407yhoizBhRjGlqsp+LW9DlG4LHKLWyKCXb+iozkdD8Bh9RX3iYz4RWNSm552G1u8YQtZkeN6"; - sout << "ISOxeylEI9OFkxvueA6u7juBnwpmt3sjcvCOLbbwwpc0JyWfAhiRdBfC7aXLjHm0NcwiULATnoJ8"; - sout << "AMfNt/qzosP8LYQCvRYwJYShbskncZCd5amEmN4eStNaebKyFX++T+XC9EiG9FysF2VZdRf3OhzG"; - sout << "dDBtLbkdALyBU3A6GhCueeZ4c5vEsHC8RMH5iJtYHXwLc0OWzUPC/DVlhVi82avRChrbJQ55qHza"; - sout << "DBnr3I00eUsfGxijrqaQ4bfLLv6S5e4jP6C6waRbf4RY/Q6kTb/9oECBuXgayar3WIvLp9txdLEi"; - sout << "dc0sokRgOMXjrheq/gZ+WHBm88SLRpecDwc4D0kGo7UQC/PIajFQ6b+rTJBKwYxeaaVpA2gaakqR"; - sout << "BeQurXrdpAW7hWwBHjzWtxG/qKwO/w1+x/zAICr9N2/1Bex8yhnnIEcltD2qN5ykzqBzrKrbAzaT"; - sout << "+6+ZV4ZTvaeeR9ELOBDYzkkUF6cgTWAZnwT0dKySp2C6FSPkpNseYrrgwg4Ddp49pN7ehwCE6V99"; - sout << "ebgMYWGWqJkBJ7ID8NSMDFjOTvuWzz08QpaygfGSGgipHcuEKdHEe+waRMOMZSPVD7uDdV67805a"; - sout << "67OTB9j4JiA9zhyXqx3STc/ICLFkSjZbl0VC5XAyVSrC0ssNFCVLp7OQ1REJYnj2bCPMpZCX7VHF"; - sout << "RY8HRrA7V+X0HGBVPAcCrBn47lFvImWGBWGAWFO+TltuyKwvuiBOU1VfG1mxFCJJj/+dv7jIdDp5"; - sout << "+VEE6pCWOJxzveqnJ31PvwrlGjxY8WondS64rNe2qnQ+grQCp69T4vHe7zuYfta+QyjtQxHiXSBV"; - sout << "Zyu+R1M648V5jmGpQUT1bek5FLy5LoDDjkHITygysq4NzrytxR7Ncmgns3tdB9LV5zcHBG2I2gzc"; - sout << "lHRBaE17Z3Lo6zdwTlGpfumcYid4Rlj7OgZIZTz3ogfaWU3OblHZ7Kqs3GGvctnHnaEtK9oqQQFP"; - sout << "zEvAG1nL5qjUKB70e9YgDiUYKAC7xyWVoB0BCIjIXdJh8JlwXMRQDQwEwAeV8FAYKQffZB/2JEeR"; - sout << "NnlaRxoQVuiNom9Dh7AokAEHuitMI+KD0n/bijH01pr9cxGoz9uimYQwc13mw+ZS0GsWeeEqYSWV"; - sout << "kUsJVrisNxDeJMKW0hQuZmNTkjve2aX70+V9ouwgMlYQusDxVGugu0HNUQBgWbnE7CW9fh8GQV6d"; - sout << "42/8i28kLjJ5wiiReXFbXDQ1XBz2lwyXXm4ITq5e3tqn/BDX5S6yQg5yKGl+2pISvDvZipWVNj0i"; - sout << "vNeVWDITOZw8OAceKLZJ0WDSaV5W+F2Io89LI4mdzDdvOk/Ct9OpxFAacJ+H3Kx2S6b70hcdrx2u"; - sout << "0Z+LB5tDRmxItNYMfz7br1fZKyAO8hUxeWN3EF6v5Kh8t0R7ZZaCEJgt19e/3sFnR8ssvG3yrdXG"; - sout << "RT6qvp2JAUyP53dm4Z8+6e/k7YA7WR1sv7d7omc56Q3LXhpvmJJPbAJ/qBXHog0pO1ypN8szYVUo"; - sout << "MbTlTlz0TECPbqdThQFSps4oXTeCcR+5fj7SR2K3FVjbxHfWhrUfw35Nh7nPfzyDHwfmX+CMdwoj"; - sout << "oosswTADZSwvEh46PBffUydxEG7X70qyn8VhwtZGHTFNTjjncB9AXeEpYpqxo9ZzOXcCM4JwI+jC"; - sout << "eDsx+HOZdie9keAE27XUcvKhNYv+G5Hq3rAVNe2GRgDdt47Ysz9vECFOBxO4RVAl0jr3TmfKoqmR"; - sout << "3WBN07KGMf2q1osu+RZ0I2wAQBwQOFG4c7IjxqEh1tlBsFUQBE207K8zhrYyURXulKfdHL+iWMFs"; - sout << "LxDsVCo3kG1L1WRBXynobD9DGvbx3EJf8eIgrKuHSEF46SHUWs6HjACerAjryYxvgHUoUez5cD88"; - sout << "TcQwFBS662AAs2ZAp/uA2Y5Pc+mnluC+5HYwxYI1hh9TVEQPXRPLccrfkitZYn/kV1nf7AzK8lEm"; - sout << "lavQkdl8e0rKXTunMcuayFgtPCyqiUPn16MkaQBfaNM9B+gw/fFeRctuRq46xbF0fTzBS8iQBkoB"; - sout << "jTV7UmXA0Ysi+iVFAod6e4o9eyW/WEhGUCrAWefyUqbytPJDH7/1VnTdlN0bpeXXSyqWgTpsWYay"; - sout << "GhdYTsPDdfTSzkEU/0wdKm/w8Mjcf3+ZcVNJhPtJpEXzoFsSL0VVGym0kDsJooOizfSqJPyczVD/"; - sout << "b3vN7al2kuDcNZjVyWiZCfPyED1UQTSQFVZBygAfmjg/spVyg3zbiW6BjcDuxOxOHmBGXuE2XOjM"; - sout << "Qa4Qcw7ioTk8dw1VobafnWiGN2S01drTqLFry6ZOG2es7IejQh6HsGig3Iis3xAxWWGS/Wazhw8v"; - sout << "M3OQzSmbW7j0L0KcLOMtSxiYo5LHXxhti/pAaI0VZSLkQYgyMD/mBdbs0B8i3diXoAM/BzqfoU7L"; - sout << "7SNslWLwXXscPrJsCwFqe2UiahyQyIvf+qXqBhKuFnIHVt0cF5cFAeEh1kYzAd0JDbi/LB2hYsVX"; - sout << "rosavz3xN715cRmjpjuQvlIDUGfmmJPPvUIbbXiT6q+TrDdQaPw56G7vz5llNd17QS7UUZymWjY8"; - sout << "p/8bq6MieVzWeOmiBPbmWRtmZ4GPTc0bmau8FH77JxwgrkdH/7jDL6uhcd3grTrsm8X8ZX9FGCZy"; - sout << "QZQ52pU3tWbfy7KsWnBZjkRwzZVRY3EVjg9V1PMQYKAqps7mU0LrDBPwrgHW0c9EvuYa9SIShUgX"; - sout << "JtiWswoJVbbJXltdhclBmvuDsyK/MfLJrSE7qnPOD009hILCojdSCmqdaFgpmw88EiYAVDqwcFzw"; - sout << "EiDdvqg91OFplvWoGEQx5wsjOKaQst1Z1LSc/C1ZZH+mmBMgHfYmr3ZiX/eYUjGzYpdKGa00v8gk"; - sout << "dDEkUCLQ1I0ayiaz9cMPWkZ3avnYb7TdUKa3iwERVVFEJWfhpWe/4KP5qBL3Ih8qLicgtdkt6FOM"; - sout << "ymOpNQmfe2X+P3U6042OQPIU1vmig0euGeKQmsE4K3NoSOEy2QheNv/2GSSNRzc925zhjAk1zxXQ"; - sout << "04KKCDA4HJKFK0tly5P6KjSVTiQXZ6e+IWH9CQfAJWumCVtHNCulD2epjCPlTEjHH332gl5c7h7u"; - sout << "eJRVbsJKZm9z1TgFN5w1iDWI1cBLZipzM0BznWEvEoYNixyZot+Yfm6IC0SZi8DA7c4Ngn9V0ydg"; - sout << "c8JNp+A7T7LqB/LWgma/E4+wv/cdoXiKi3mwg71ZQRM1d5oWknFfm8hq0KNOl2RsSOYO92PIF8Lp"; - sout << "kG2KPK2nBU7VkbNtaJwjmJpadvkAEPPpCbYDdRjrvKaSw9yehsWKfLVuCLQHlO7uJmGe2LHWKojj"; - sout << "BtYYau7+Vfyjj8K4eNqABvny8hj8zV0HyToirIxVpBmH/6/8p2imqYx8hc7yBE84LSNCEac3UJYf"; - sout << "8sxZluHs/ZD3t6s9jVisgtx67iv8u3A4toAOlYjBqj/RXVQRAb30MzAFy1YCdxedXIvKBa/jXMwk"; - sout << "0DsQ+YcRsnKmnGQ2yOxNVfiJZIg+KqN2Ork0y2QNEy7aTv25OEUM8U2wKaRkiyNYh9VmvZpYk5xM"; - sout << "OPOsReX2FOJOFTCUGZ+w4Lp13OI+QLc9TBllim4bhtbBscCMN78WOpnUccS17gXHnqtpK1Nx7R3N"; - sout << "dwpVTYBCxVVUSCHAREPvmPlHj7c7nnIfO5LRxuGxrg0TfCBUwCNiJvuUpiuqNyJha+eJCPS+SlXy"; - sout << "BN9m4jvEM4KGbkGSwelXon6LWOKq3cya+8xOqQ6KliFrdQaNk0Tdg1Mi2oRjBlqjyb55oXYNeRgT"; - sout << "wQ8wAPqY5TM/z/Zk9ELGeQz7JjS/ru5Q1LGNUU3tL0AjqUdU6adovph7CC0/g8lX/NqYg1oRT575"; - sout << "7m4ihHqVTIN3pMkB8TJBZNAa3i1rlom4qcOFGdjTi4WTRd9r3ll0y4PB7V81SddpOk8RPTOIDKR8"; - sout << "pgIa6xFOQ/GTIaB8i7uLax+IMs4dIq+gzpxE9g35BP7J5cbIYJJeMFIm92l7wJB93ANTT84yQABm"; - sout << "oBnlfu/fjcbGvfHw41CTRpMWraO5d9nABxuIX4wFKEzxse0iAVsJA21RbpdBtIHLBcbYKCh1bUW/"; - sout << "hRSk1yislMs9Mtjx/VSUn50NAompaF3NLpG10/hGd7pp1co4wZm7+fiW6sIPBCqLtkg6A1+qGUxG"; - sout << "nUU6NqUvmyWo5B4Re3mDggASRSIFdD8ckv7LlAbyDgVyIxsBrf80ISQN7jRDDd7MjkyAJ5CRlrUu"; - sout << "vFCwCo11+7E/Yfk2dadQJTjGvuyyDXW72l4ze1PuBrjnql0vBe7CCPZKgKJWWDzbGIEKGbBqDfBV"; - sout << "EVA0NrVpWYBu3uhj8sHi+cAMwiXy2T1ar59Cz8bvuIA+9egegV19YAay92S86BsJbhVfb/THW/G9"; - sout << "sbTgF3MWTUgvDFmaJzYn7mkR/2xsBn6UQsLYPZPxOQ4VaEUzAKFSP0zwGZfMYE/REnDIbdOR1ai+"; - sout << "zrA5gWXmbLlr09hkM7tIn2Tw/+X7zEzZ66oDyjDs6g4a+CBt8OtOLi7Ga/dO1DP2y4n1YPAsGADW"; - sout << "Jf24rVZGv8IWyGmlYJZWX2tBKV++HDUTxnmbsDSI7Jy3r1UXNWskyDC9zYWetsp8UxAVRYBDhwfv"; - sout << "fqlchxnmTpm6ozULqYI6ExncbbbK1IBSbY2A+VBUrEqdH7Rxg45S0HKRbvB2jAkksOyhbXUwngM8"; - sout << "6uG1TUcRdnxLvZniKptRcYJthyx0HDHCzLoTT1AlyTB9kvrktDArOUa1zmF2hCZAYP1swqb+zBjP"; - sout << "CHd4aExlUf/UokEBduXCWomJLGXsJpVG1EVz6kTZb2Twdt5x5vZJK3O/LCBKgZNMuS7/xLFZpvsB"; - sout << "cUA0MoO577pa7SECGSrUrSblkliCMKmIECRrQoEJaNKCs1r991ptT9aWnLSbl6HTTWXKcceN0vXQ"; - sout << "tKwTpiM5nhBmea6eOQn5JP5oeytyDul1Jd0SHR4WILgQvYQSrqdyQOf5zDBQHe2/EsinsfonJy50"; - sout << "4M4l2ArPeWEjJKtQBpgQaLF7CRGTjnv0TPJ1Xjo2Cy2OFvWpkA1ZjK3Q/I5PJsMrlkYkTSgQql/k"; - sout << "HwmLiFL0KGdVfo6F1HT5J/5ZxCDOWhO+DmvLXtvTQSEE2asJVZWDo1rpKOq6H3AhObvUgp8AtPec"; - sout << "+KXWiyzTH9YYq5RFU4JE8a+lt8+bGtYHxGOE6AuVgQsvUvAJ2kgqgi+kLYF19FZjeYbvuxzI3t7j"; - sout << "+qCO3v59HfjZWfQ7FOJ/ciLVAAEM65r1sX1JpZRodNnZUvbxT7QoVOm0LwjW3qw2VxZCbVU/jPkc"; - sout << "XQ+8lxiZpHO9mSxBXvQkwRW4K9MZ9dBl0oYdvq8IK33SkyTcaHBqlp2oszKYwDFyLo2C9uiE2BSE"; - sout << "gj9UpPG4M7XOUjUMJ3gs7aitPsTdI5oHG1xklLOTvyWYIJ6vk2/uBrMImGSExPSMMGw0NEbHFEX0"; - sout << "Fh4Td6+cO6QgCQeobzV6Hxsi+HTk5mtAbMGXiIKi7bAiSC3tkoiCeDfBxzqROYgSVQU9fW8PeyW9"; - sout << "xEmwZSABCNRog1xAwHaOuosBqYDMidyh7F5ID9XeYHH0qaMxLmzYkZ0SAl1qECFzJ7wUSiwRzvzC"; - sout << "VfatrCs84cDUKQIpr82LPouxQBeVFQAlj0Plcuo6CxrJgVR8q6ciC8eCnZ5WaF27pKHrfPc7ZjdL"; - sout << "h4AwTLGOpUg0N0/aUJxvFGOVbm/g8pH7duFC3+ycmYrOXZPbhz2qthsxf894+lkU6TfmN3OOoTwG"; - sout << "EXrda/bR2NQNvqJWBRTv//VdXIr1RmlNMDcz2d9lL3rwF9Gq5EupgYIfe1FhiiqDjyrTzh7fw2Qw"; - sout << "P5T1SfKa5Ww9mGxa1psmYlJ/IzOQlSlSzSHxMVvD7c6UDpWwKWFlzzRPjl3WPS9Dmzk3tzUo2ZYx"; - sout << "RdP7RBZg7Cveb3lkIn+gU5XII4cD79YzKPesFWpWR1ejkLVGW1YQB7jtRaJmoz8T81UTuqhxhBt4"; - sout << "NswM1L0VtG8asAidWd9nT3Y/WWI2ydJ9YgiepKeMdtpt8aYqoutZUFzPKGhdZsZIYikb9r1Svrd3"; - sout << "MAyceFX5RIKLVcyJJC9n1QlJVSXz2e47rrj99prVvMN4ROB4hpORryHzGuPMMhpn21ZywDk1aH9D"; - sout << "0WGZq8Nm4YZEjneUtkkW0nXcF0nxJ0wTFBmOrXEgjpk6jzfQ+9+b803WlwtqRUJ6S6FYirRyTv6X"; - sout << "r8xCqyXgNioZycZSgjxzLcoFqq/65u935jK7BEMtBGWa4zQcwq38IcmZu9xfCrQLCfHpIfydjxBs"; - sout << "8PrNV84ccG5Yfs4zN6oaLOahJBukoFHUZLLM+67oALQzsStEXS+HQhWMJCRj5M/8/SwoxZM0ACfQ"; - sout << "XY5G7OnF21/5NwpsUoYboT1wNFT2r8TKrFOx9bEIbKV8xTUgdrnZwbKGPemsypnnsFHoA3BwIWKx"; - sout << "+w0vNWVqK2vsbL/pTiZiov1lxvfKFV25Q9uylplUaSnzYuaDQFLOPBFr8nhcmnfZA8r4ljcjMNw6"; - sout << "0AUHO03MyUDfu0BUVYQEyGXpEvwnI5JmVOI/y3/TqnVnReemG4D+diuwm2Q5UgyY8KyykxJam4Zv"; - sout << "W+Tn3DHT0emJNWv3imi5itW0rUIaTpt/9a0LvqIWi0Q1OOvrxGgUcrsRzfUVi+ru+tX9YZrBOYXe"; - sout << "Ut+KwUCvITG88r9m7o8aovNyH9V7bC4axTrIxmtpRiRP5e0Z/IiH6b560Z9ixQ0MRv5SUn/lhX/l"; - sout << "AP2rTsqxLmt2mWz9b3GqtEO0iW8XisORLiHzupEX9jRbcqShrBoG9bu+4DUO9hGdemq5lX1782dh"; - sout << "FeZOjhIE0/8JttTmz7EZwzp7SeHup1yqlForPR0hhkFzfOOQk5CwfYR1tD+0ImdmbL/QyE/TRsnP"; - sout << "NoDB0NVyXcmlJQzRH+s/dK9kJjHFn4FAUJ7Eaw5xi8/O5VokSz3gfwwPHbKFexrY18fDiyeZ404M"; - sout << "cAGYSGyYzxkpa1HwuZE7UzUNlyzpuja8RR/8XaUv1XRK1lIkmDFA9cKT81vtXJBY8/04Jh2OsPBT"; - sout << "mFRnFF6+vItwlsmOaF/WUstZg2XxP+MePh2jGkKj3b4c4Vw9YwzD0/xNlkCk22jnN60wtM5WHiLT"; - sout << "Opu4ZnP6/woOP+7uOOHdjEJvo2d/v3TOmNmfx38TBCBdAbRpMzy4XDZdsWpP7l0DN9/OnbErxD30"; - sout << "2sECWhkxKygkNxeq+CoeiS6Zl3mzEKkQ9q/XHccCioWgiODFt6+Wg9MtPHGaR1cnaDDBBp4YQWJG"; - sout << "c0D1Urc9H4Tpn0lmJkx0p09nBoMHGIBUKmHuhMOnuEuZp9wDYSQ6UnjtLy/+QJcCB8QDvTCg9mhu"; - sout << "W2HNZPqa9DFD2tmV5e3+pTojHI4O0tOwA5B2OYmqFzdvMWLuFn0uMHOu5285KE0ZDykOOv4Nupq9"; - sout << "z/rjTtrxInMTU+a7hynQ7Ra75F5nCnEefKXL2lgD90IkuNDpmxHBX5OgOYb9RUwqcVGAFSZq1jYm"; - sout << "rg7sxuDJu/honmXsDohqfo3/vBVca9U28wnCJ2IsuJXRQwzBLOJAz44ijj6+Bx4R/7U3IbN6nSNW"; - sout << "5fXiqV1JD/r3AMAf6THZKnYy88mv6kMzt0ZXPd4PlyHCYhLTIzw3AZP36xPjEWBRP8FGaJw5Pm02"; - sout << "Kh91uM2QdF2jowNV/Ago7rHS09R3B/aWjmGdVTyhCjdogPtrhVHdaq33OftcCuCF7q/wZ8kL56HD"; - sout << "2T2BxxmsZY0XqSlU3757R/VoeGdk7XjAGbpOeyxn+yrP8/stbyIwxq8PSv6YhRN2c3+H9AYka8Lr"; - sout << "Vrb16X3aEEP49jxNwEOL4hM2rcgS7f3uayVLwsuviWxiUcQdWcmjTVgTos/11pdXpSaPqSAvpAMs"; - sout << "m79R8fdq1V5+kmZZ8UZKUdQwjJxeg83dCTDpYRTXAdSdKDLXeD4Kkvd2ebKOGb+1j345x4HUnuef"; - sout << "ijTom/Xz3fEw1jLgqZd/nVeUCBYcXpozL50LwzYDZzjxERSVCMCgmt6VJBgpvwFBXyAYJql5972d"; - sout << "vIP6cFU9K1AGAUipBxgaVR/5NBFTIjgo2CEOJulAQkwWXE1TOUXHF6hD+Tp9Zf3nZRmTBU2OXA/U"; - sout << "8M5Q6nyDRaV/qRdB3GMrYPHZIXcy+uh9xI6OYDWSfv6uZxGfKDzN2JjMNLuK5CEIu4hiLj3JHC5o"; - sout << "vHJ77Vimbm7wa/cTYx4ztSvSSFwSXy82nSd3o6d3Z7vL4FlzDjxkW+6AfP+8SQLvC8yiKiGr6U9/"; - sout << "cqFdi3xOg3Ska+3F1sn/OIrDZVLkP5eyqo2aDC++WnVv4Aig14Xf7lgD/nOV16X4sn6BmpIysM/i"; - sout << "Cjqh7AuHgDkB9kgr7jQXFeGAhdGCNby93jgIRBdWTY61JRX6Isek98cdoL1XqCheIxKckNMmljnr"; - sout << "lHL4Fi8okkonI43IKV+8NJ5eH/JmUEtrcOuaB9M4rEI1NsEQK72dIWyuuIYyocaeA87R2dt2biia"; - sout << "SsZLV5jE2O9YE/AWY5fDGAH9PA40TD8bC6bxgaik/QFegoo4tRw2yR+GwtUh9Utsu9LUofRnq6KZ"; - sout << "hq+3Bqzu3U9grOYNU9MUxNo5jZS52U28A8NSuHBDeS2870sdUvEM2RftmNusMnCDR1bw/tru/i33"; - sout << "iYIUoaX6EPN2UOJMm1TGqIOMs4QWkDTXYAsIdpBgq2nMfXVDb5y/nqpmQH2eJCgz/7Ly0VtZISf9"; - sout << "mkDrrok3cIEMKMgvM5X/dA4F6Bv929nkXqqSi6AtUER/jJJARkpAJHJTj0IxUgY2unVx4KP4OuL5"; - sout << "b1HeIkWdlln4V64OPJnBiKl17p4pX5BGtEXngsMR3kdDBJwliLv6ciLvPKybgggvLSQbQ/vqnZdh"; - sout << "1x1GlnvrpKfoLFH1S6f4HiDicapOP3wKOp8ECyNZp2IAVbRmmPhjKjPry1EdgySgpXFZUHVEHdR3"; - sout << "R7cGnH1AGhZinYuoN74cFsNooe+LxT74F4KWSW08+S659M7862DADzaxlHAbd9BL79Tu8RGS5CUB"; - sout << "i2ATQ4rUgRvzC9OGcwCpOAfbp33+jqLmAWRllB5f5uBJuO2nG7OiKHV9jeunOyHxNnp4UTpblCBW"; - sout << "yKx4EVA7p5T7qH8krIftUpBbUx38XhCtPMcsIZIMp2w+nmrlZeHyPGVGFrA+Z1+VjmRtiwGCghXG"; - sout << "JjZ9j+HPIniiHK78MNAPoRlt4G5mtBopMopDn/1cdVroLuJkxX1CpCOu3MmkQQghl/km3jphiehr"; - sout << "Bj/E9IZI3/LesK3Esv1J5VJCFl+Eyn8i90S2ij6sBBPF+eqY7TAMYAr7MK9mFtfM9orEpIJTvG+U"; - sout << "Uun4ibgHRw0t6UQ4nnvZKHL4qmZXZB4clGgvF2Sl1hj3FpUhQLr4YWJbMeBVddvi8Ifsl/KpN/J8"; - sout << "k0iL9nhnyDfLBNy9iT6Fnyrt6wKEJKAmvMTUS/U5YZ3CFAgWEmqI04ESYkZ1F/pVjwgfDtwjbkPV"; - sout << "Vr+9zaxAa2gT5yZZ8m6CqMJJ+Hr1857AgVw+msikx4c3G0zFZpUEv4o3PiprOr0t0bGSzr4rWnLb"; - sout << "rPNfMq0mQI2bWIMeZDtiAeMLOwrGdlnBUekp8NydJeL1XNAMMJjHKvNEnopNqkZ9Gt1Kmvl3Eqcx"; - sout << "TuwbsX2ew5yNJOUD7T9q4lYRdKSNs88fdTFzE6heGTV9UGs6z0PMP2HoLXSShBhvPAIX3t92KK+9"; - sout << "8986KZvRZ/QVN93Aqt1mczK4SEgo2X6Tm5814X6WcCWiZpH8CcwQjW23psQyguC4PyLhGJo92XJY"; - sout << "GRVVisGEARz1qjm/GY/jW9kE6alXImoLE+F0opY6QTQXAo+KKleXAWmMEUs972E/aFQtmR2V2mxo"; - sout << "Ga6EtSNXobw7jP+kMkYoSiav9pYhII3t/v/badrNBhKksNt56Zx6BYFOnfhPrwGkR+YoRz2+x+gR"; - sout << "GMHLwRFe8lMPR584WNMrC8r0r3i+qrp2TeG5vFM+VKwWLSBUoG+bgZShS7ITSJiSjkuDMpa7jy3w"; - sout << "1s1AIgtNyvxrKPTKr6C2FjVuhDkpSInVX+Cdr7utecsExirDn7uyb46oI46oNbsD/6HW3sIkvrVH"; - sout << "xWY1bwvcKlT1+aFWKYtdZEOk8JE60pwiat+MpxgOk9TC5EUdJvcxOP8M8zQsIuQhWlkgNhsm3G7X"; - sout << "1xWL302yhLrl66DlkKHjCH8+ee7/RCRm3l4nS62XeVQ9ZlzEIUbPP1tnnzg2aFi/VxMxk+EfF23P"; - sout << "auIEXL5jALZVnr1D93LlSiqGwKoAq7s1bblpIGyxcXOsuaZ8Ls/J7FsxcLD8BQiK9v3wotEzaOOD"; - sout << "jSfsH5DDUQ/nIdlBwZk1J8aOTSIo7DZ1wZRBSz66ptR9sSqcwrXvFndHulLtva04DfNDPzPNaCoH"; - sout << "cAjxsOZRKl+ZPi9X/qsVzCr6DijJYeWT+UqbhTOGX/Vrl3M3Mt7q4Pcs0vcdXH+SUV8YlF/zmaD/"; - sout << "xxZcFz5DPz8NRAculqklupW4Wc1D421TC78Kka2NTJKLzivgisWROR9c6g4ml5cqbrizhraRbvXP"; - sout << "ilWQ0PSOq+uyJqeo+1JOSH3KWoyGWfOfGxlRffmvsOOuY2oblonsSCz6sYKySsnKeTezRcSP9PFg"; - sout << "6EGCjFg1WodhnA/KWixJtQ8QsfhaRDoIYklDBWlPBEKxgvHi3yVF3vTyakoY/VR82B8UWfzxW+gL"; - sout << "ldM+gn3H/DTpzR8/h7xBK2yoOw2Gpv2TcEB5oMtPsXsN58QdoqAsgBcHI/GNdfqaQDK0lt+npV2M"; - sout << "8NAnFcK3r/wdAJ6OwJYtXPEIbhHxRkEY7GLUT1IwLeV2PIV8gy3Eh03ULZDcmii5xO/MsPgrUSKM"; - sout << "Z9s+JmBmJhQISUjumZ2SBkAU5V3SG3T8c8EaeN/9yNYMFmSXX/jAjlAViNK8bCfnPUkrmnTAMaMv"; - sout << "blW6tw1yhXV1WSAe075/Zl9slrZhcCJHYX+mZ1yKDpt7x/QxayGmwypWDo9ukTnE66UKiZ0udTiE"; - sout << "awN5uFe+qvj+zZLrnM4Bf4ZjMjKzjd67W8UaxMJCVtx2SHeNC7Ffjo9xN4bicDUnpqTt5phMV+hX"; - sout << "lBRiuUYA067qUb58MbkIE02F36n/ioHNYj15Eteh9wv1IMIpMLz/1UGm6+97m8xIAKH5B691KOsD"; - sout << "k1ovwfjzGXmFX12KsdSplPSmH9UCo6CsuJmFkhAkYd5vCw99+JVu7RNqhJGF4h6Lg22AqisjfHde"; - sout << "5CFUks0DocGrgDw3GcmVMFAV1Ix29iekfAgEJcOphJBGJ3FvwRmGkflrIJW5x2sPMaRHeU8Fkzso"; - sout << "o/IF8537YleWU45S1m+GXNdomkGIFVXZCKwiQ8bRr1MmzMcs+0EkU0r6ei7yxi+6LlKfp2NQOv/Q"; - sout << "owQ74v4YHvyNJcI+YLLMHaZySYlxIjL33H6b8vjrzsDntYXii8lVRRXhykAiLldwH+89cFk2tbeO"; - sout << "am9kZeEIlCX889URxsw58TRlwd0Lfag6IhNO/hHpi652lgEYcZu5NPaDQPRRC6vkl/+ocoMmTDhX"; - sout << "uGhphdczLsCBpIejuQVqI1dhrnko128Jzl6yIe6mpuhzRT1ReFL80F6VRjegnDa67Srztleu6qzi"; - sout << "OUgU3EDRfE8IJ4i97SsnTFJR7Eyou2/GP1U+ai1U7JTknHbg7fNeD5wjTIyQVE31oOR2WTNELWa0"; - sout << "/GWO3P6Em3J7vZJifeKTGYWf1124uh+oBYgv+j3vUuX01i1P9kmeUP0nmVgzWs6AGIvbIp6vF+gV"; - sout << "Uc+8TvNYH+0f19q/lPOBnBT46URHpJ9EZxodLin8K5aTCB3sLjpXiqQDPD9fbMzL2r0s1Z0YPWgH"; - sout << "Mllgxh7l6bIhAmymXIUX3+Iw9rnW/6ODO1huBbvgZOAUrLMnTA9j54trOL/n2DxwAU7JJ/fCeH5u"; - sout << "PKH6nM9l/PvCTslH7ahvT/uqGD3/++3FeHok90oU0QxyDVbsMDX+ksj5Hn15ZQZQtYqToONe/OI2"; - sout << "F+xJHU1CWejRZDJgDICcBYQSI9e6crc1vdf0y7aUeNRthj2xLC0zbtfy7PaIEy/R2E93I3jOoAq2"; - sout << "Mz+0icoUb3Hckf2St+HqfYPL59yCrEyqoFONN5XMArC/MTYpMn/XQfCQL2X9xV9T7WaScs9TAqQ2"; - sout << "/qZrrcfRxUaPCVIBtG5V+of/vfYoZzwQrLTioTohrX6SC0WvxS05UlbYo9sDwrchmIxPmrf5vI10"; - sout << "T4Sc1b7sRfiijqZA+YTnYyBPG5c2qqsmSXLmtctpzUn94XPhS76qLj6uQEcoaYTOwFK2dVTvFpfu"; - sout << "zlRNDzCCfzXc64lA9oKwEhn2tTH4ddClyYobly+2HH3xz3NQMrLxeYXblIKBFJzn6IlY26a+77qM"; - sout << "srMzEwRmnzUojuRBO6n7BxGFfP1JuP4FhIpKLTgA1Ql5mf7Z9LrsM2lORzMuO/og7LvjrO5jVs/w"; - sout << "+ZKmQzSzNBjEgq09UDk3gMG5OV38SgLXQCkUnCUThGU6En5OTQkSAF0IDQ9Bmmyadqp/+V6WiViv"; - sout << "Py/WYbFDSAb95ME/J9YGfTg9VWncLDEU4otj6Milbr8OLjlR5YbbGkieo2I4jYfuKHR+/C1uXVWk"; - sout << "HEOZBv2lYRKteXx18m8DLOZ1PO0PC20KRwcep1QSmw8SH0NJ2Gj5L2WvkpfCSwKYJOJECrAc2PcI"; - sout << "Bzf363+QgmckMylMuHJ0Kf6AAP2xFtKYw3qM7tTUjt14QkC1Bi0l7ViNGt1vvyTq8erWFJYveVwm"; - sout << "lJQMmyvrHq5g+hbj9ftT8cPxbKJ/EPYGKrtrM8AGxTxrtA0FHqmzjPTm9rHXhGYfm3ALWw+frbVc"; - sout << "d06D+iiyRXpTTDZoEcbMaZYCSyHI1QQmTR9Wfr64r6XSk+/jDLNlL/Syp/Hzs5dK3P2YR/UjTB3I"; - sout << "8U70K/yeV0v9rDUI41COSK/akYmpybimqmexTop7zqNUIFOqFHbH7TCqRxfq76DFebeHbE1Nq7l9"; - sout << "p8HRuOuavv9wKGBJx5BoYl0h+ujVfqWIJHas9QuCbJK8Z2eNht/j/FFSvdlPfWIrRu20Anay6EHN"; - sout << "bEDl6/voathXXz8Rtc+/xkb4A2D0+WwXHrYh0onectahnSjI6sK35wtf8UF653KENWSOVFiFAwjr"; - sout << "XHV4YZb+VWi5jQ7470jOdd+6eg9v47AymA+6Z6ZeTWazbxSgfV8hCsAepHyZ+U2Z0B1Zgeuc5rKp"; - sout << "MhBQZ8xf5x+yM1G9Jtu4Z5kgQ8NdliztMLkquT+mbY9U4eri/WsAQa0BvC6hisueLg7IGqjQGgIJ"; - sout << "29Y6iqD9fJZuGAgbEbW7qu7J3DyYzGaKyAx4+GOyctio9ChW1v4uq7gkc8XNpmYiGl/90mo20hzR"; - sout << "vEXjcg3Hh/aHJlVXH0hJ6W4kOuBFkZxptoDFi4vKdSCVWAgnQgWhmhkeI492Y9bUaDTxejYhTfDX"; - sout << "N08UiRXnAN96KK9iL1fJZxB5MUX7cJ/GvW7RjTVP1MNzy/FdlAN+k6Sb8vyst3Sz3eGHOgO1hpTq"; - sout << "Wz9Moc4BVabx5NLlb74ScQHCHX3dnKXle6sPCzfbPjM2Z8rbm8NvB/VH1FZ/EzriephAUYdra8OV"; - sout << "+f9V3o9zEYMGujKxqZysPfheqaA3gKKDVgKGhfUn51dEq5YC61aWqxjO1suUrW6rQpBHN/V/VadA"; - sout << "hpuSHr2UHCeL8yUzC2nnXxpHCifGCrx1/R+A7EXBjTlrxd0n83pEV5paIK1AB8Vsv7RNrT0bEPxx"; - sout << "uNxgTVdFUVHVLZf2zuhlqQCSr6zKzQgdlsW2Rbkzqm/RpdwRmONCWkS5IRTwdqLM3rfStz3zt3U2"; - sout << "PklWaPeSMTUzREfa9xfaxA99Yn8QcQZU0GK+zEe0d/iyeDr+7XPYH6ZOgQbFp3BgHWk9tAgeVx3d"; - sout << "hOwbut1u5WRa4azjGkYUUlvXLJpehNwKEiaej7u9jKrdQMTRi7gtSYliGdNr0nnw4ADV60LXD0Fi"; - sout << "uYvPQwJnbICCYdy/Yx0X/HBu0dUS9D15JuHuT9z1D/dqirMKNiWhXlEzC99gX4/mGGM4Q6l53SQw"; - sout << "0205XkJ21NasDxznXp8UE2e4GsI+N40b6mLvLZaauw9dB2IeNhwZvn7mkGDraXjDnpnnmX0iV0Y3"; - sout << "OH6OCq5ZBlk9/IIVJpS6Xtec6MiuVBYrhKj2MVn0tI7rRalXXqk+7Plmg/9S/Vh6U5RM3pCjTTf4"; - sout << "ZEAxFH7LhT/JRpkk7fPxcncaHQVrnrbM8xEYc9/2nptKYriJdXHXecsiH8xYjjzw8ACs/FeFyPsK"; - sout << "84MjAvnsSWnPVpJe9n51BIQ68dUG87+igoKtNjWomVVSAMlBKtbiNrzFN+4AvESKCufoTC4gpGPD"; - sout << "2sFkTO5yg8+v3rwFK4aAjBLA6eKzSGkcJqcThom+NfRjrvCtPysEisAdXHc40muhThMiIiOFXwbH"; - sout << "T4722yaSHEMGNwDqY9dlixhGz8L5G/B3hbrlqCPe2+GHcmmMI58oFMgZhEh4u/1t5nkCDizZ1COg"; - sout << "+CDWA4n4lcOXfVMwp/8u7GXZMMLuGSJUy38DgjLzy6f8bji0+EEZ8rbFsvMjssgcx25YJ9Jy23eq"; - sout << "rcwGu8iMKUQ6t8J2sZFPXuFrn9cyFP/vP6qFNWZ1CtiN2fRVbqwtBj6DXmSae7NDMmL8XfGlTD5i"; - sout << "aiIWK5SPqYzS0K6oPFagYWYjIKqkNRyIAseq0fqTLKnyKmv8oj///TlXGawbehr9clexoKbAM5Mv"; - sout << "1RKu3/pJiPVwOPXcwCIlQ+BemtjmsnBzJySAkA1I9mpgkPm4Pg4qblWhgq/aVulwN5uSkJP4ZU6n"; - sout << "reWj3khTO6Svsua3toMPYm6FOoioKOOrMMXQCoIjsybBmSaEHeAFxZmHODtbf/WrGL4YbhUMal5o"; - sout << "90Ay+c0apxZIV+8d7Bm5L0dRXmQfSUCnP1CKyUEGPaMAPK+DYRZNU97lzpowFRdjTlrJX5aN3jrk"; - sout << "WN8/xaU0PTVo1LtIesYvyG8n8gBSmj90hw5QLFt/94H1NDvPYMvw5A6oGoQUYdCXTXzrRJ2OEow1"; - sout << "0/qEzLCWExSS9q8nbsf9Ne5ZC24XA9KHmiJot5v7MZ4KBORjPy/Ub223U3xuJqVGHlSjYuHqmOFC"; - sout << "bhqGY4yNo/7PU1/cvg6LbtQSriMj8+85GQSheMBI73EnwIHgPOIV7EGEr/GHrfyM1jI763x7Pr0H"; - sout << "Bcsg5o34glMy6YWSMLd16/Djq277MW4XiMoGPK7Rptwh9ahtgjS+SAl92VTVyx+kr7gZXfecCivl"; - sout << "gXUmA8sQugTC3Zf7QqR5eJvYA/i+PMRWkLw4YCogDOYXK7tKY86djtBHftkYob6bMj8a7/1/XGzm"; - sout << "Xz1OGinw9yYz7N19wgkVZEubmK08ZnKINN7A9IU98iAauGaLizHRlQ1y84Sz13coFKe9lRtgnNHr"; - sout << "zsG5yqiTSIlH1jLyxVh1O94Qj6RlNT65LPJa714IW1ot0XsS8QgX7Jazdb8mqyiBky3/w+VoLWGx"; - sout << "kplBp4VZN5gRvTIHRBq4LaoGgzc1C5us3loLsxNUDgKBCBjUkywWAD+l7sCB+2btJo2HAcmgbNBQ"; - sout << "zITjO+dRirzIS6GerX9+zbzv4lC2d5K8ZmDUMHV2x2NKaXIU3mP6II/iBaP7W2VRx5qfAAChmMAP"; - sout << "B6zDwzc7LNopXp/GgOKhYzjctxbbpOcMjP1gnQ/2F4EAyXOU+iMZeXqlmbJcXk9S+bCK/Q8i96kW"; - sout << "k8lbmB/ecQxbYJKynBJoijaSzeRYL5wC/IZyqScchpePu8J84p7DEu4ECqd9+vhjp28/u6aCgnKe"; - sout << "T0mE9aIOVS9DLgIURa5qmWUFvolbrZ698AwXlEV8vkMgSDZyxgRSBdyZcmQawO26rEVQWeAr7dw2"; - sout << "zQIGGtM4wACd+Vv3e1FgjvkytZOwyo0NLlURyqdykYhZx+youz/Kmmri50XG/hpD1+5gXtVFWUhO"; - sout << "V5G9Aw6GG3+trxtMJnjCdkaA7DTjTIU8nUjNnx3TsCqXK0OqDMvem3e2TTeXorRPe2zYbhYB5pVY"; - sout << "lSJ5yYpUrIzJCb3xPGNxQP1qVv/tCEq7IIKGqPl2RS0NihXE1uz8xPMu3cxu1juHsw4Mx3qi2C3z"; - sout << "KH4LvPoEbP7fO9uELQD6ipi+AmmB7bKabD2AwsHFurh8oFtaCt+KuVy+676ggvwGEFm0j28Cj/ff"; - sout << "SQzCut2j0HQjychGdngbYIF3HLJQNr8rbh7k1FPpJhdpJc5lH4QXMddVxuydPbwQuB5zGOWZdc6y"; - sout << "0O4zjCgP0ibSPyEdhkolB5T7Sm6ftL3dctp49kJKtIyGd5scyxMreHGJxeaGADdgqken2Ahx9izm"; - sout << "VoVdjhj2tvLlJ5FbAlHqir5ZcZIu+prlMkAnW20mXjeWdh5lSCUFGcOTTyxirXkUfyDRFVwfMnOi"; - sout << "Y8j4v6X/LyNIKgBPY5qFKfbwAjYWyAPYYQVBurLEu5gsYGSYhmtm6BGZ39gnxbBfhB/JkCyE68Pp"; - sout << "wwAA3SA3Uxha5yMTYK75ClGNsWyI1AwnkfYuYgEyCbBv/psf+I/jSpT3yvcEDKiI7FwToriSgA61"; - sout << "3ksBBfw9E7Y66wmwTHq9O4alqsltIAVLkj/Lp+DxSc6tZn5lI66aOj7paImjnZac/GoJAxVUfcKL"; - sout << "q8pBZEwlJoAZb6T3wCuEdgZlYp3MZ5uInqAZSOsqQQ1/S1vCj5pwdzHrHF6Q0FJemS1AO2/GCk8p"; - sout << "dEaniaohFtWT+AMdEQi5TLVmtpltYP8J1Sm+U0/TazrQStLUXm7Wpc2DkMdU3cZw+ncK5znAzzn4"; - sout << "dQGBHVR7TMfIpx8anoWGhShI6oHh4zPXYtRtiV0Y514GWxAMB7oeLOjnJTWeiwO8VS9IPCjcKEbO"; - sout << "zTpPAwMiBrZEnglnKmIGxQXRmGMpag1JZfPRj/XyucX+LreWYF+PabGLPrjSnG5e2D4aDlNx9wGH"; - sout << "raYpvkG/LJJp81U+s3VsS2vUA6uWgz2XiHonASuttcV+a5vo5tcKUDXDaFckDzjpIJe2rn+Z1Xxo"; - sout << "X8OomKyPCCGmMEmE3SxPkGCkTIDrbb5Pc9j0NDVXbRyjVZUf2hrZ7pR+H8+jzUVgt4IfnB8Q7/Yq"; - sout << "uKW+YvA6w3hYv94BqXPHeO6FMLhH14iBLVLp1Yp2aOp1ehJUQ21OgrhNGjMmF04P/2EF8DXy2V9s"; - sout << "h+ohPrdH13fyVrQWRUkG+7Fn+GAYcpD1K5jvRavlli9pHys/axwdu089ivNpA5D3D+t+SHVGEw/L"; - sout << "ZcWpKRQrUZnOLTNjJEpEIAkryuxqTcGDzZlB6ngOVxK7oUiwzocC+zjKtmafaH1BqxhX3RxkXy/8"; - sout << "sXA9pDQ2HIn82Y7vv1+9L+88KXcTLTIUSI6iwrL43h6IhM018jTNfCgdVx2Uav7Vax1kmu9O8mhT"; - sout << "NOxDk0uYoMJfLPhr/nSWlKjgojoEj4IBmALtiMtqe8L4zNpHTRxgvLW8wMQAyj/woN1mLxLb7Lxr"; - sout << "UIY+ECqQomA4TLPWXuTsLMJXQyFgd4nQgqVTL3PUdAtwes3xrHTODTYv9f5CLEiTBV3BP5V6lo5O"; - sout << "Wj50oMM6yJWV4jZypwEp8osWHksLQjPEubu5CiBucOv3abc3Zvteygzp8J0la8wjOT3cd4+LHFwg"; - sout << "hQjO7JuNXwM1xhgdostGxjYExHC6KB9HDG3OUQ0wtxsrh5QITF3hmlvkE//xrWsyDzTcbWCgG5Wo"; - sout << "NRy0RpsXsMImYHlzO3hV5wQHz1Xjswc0ATMArZ+YJvXieYgEyhkIIzYlFlc1/GgnScyJs5Al2mvs"; - sout << "5GFjwMfC7wnMNRmV97SuDzQjKOiwMFochKCR8pSbMzJ4+dW0W9LMvasWGVAnwvQdfIm+E4+O0W2N"; - sout << "ArEB7utReECczQcBVWjdKqK6/N6QbXobZfvLto8AqTTw5CjOgTpoEybY4og+zNemINlmYgzuU7Jk"; - sout << "IwiyzZLohI6UaTrPWd9Ck2lLn9kJf07TnWVw5+2gIqhr5M0d4Yz1Xlgy7EfAWMhPEu6MuD0WKKB9"; - sout << "1MSe1zl2pbq14zDGyICSCXCLTTnLjc5Yj+E92s76igXq71sobtFNGzjQfZtK9VLTrAaeKf4Ql4jO"; - sout << "KWQJTFYbvd1ScHzbPyF3S/T0CHPDZssQQTd/VBOuspVr48Vl2/MRdLpdzjd2b9gwCh2EwGeJIy3o"; - sout << "R+AdmgHfA6PwhJKBkIliNMMR1DEhwN6QDY+b4GKnRHe3+k6tChrTNUqZpZ7Eyhret/Hz//LhmPn5"; - sout << "EJ7PSeJR7K8PKE5WoCzr0h/o7SeiHi9qi+2STfd6J940/2D4HBkYvJOEH4Tu+1XBPi589dh6vnhP"; - sout << "aQkicmf2fG4KuHO9f9Jlt21I4ksrEaibH+BKFFw2Rq2cHME8Bg2cG6HO+TyRwIROhy9yxtWQEYqY"; - sout << "g+zU+DM/WCXBCNDCPqy2q8nY19izJrU3R/9ZCUF6Ji4GjEzjjb+mErVhPcWpaLk1FWAPxtC/A+By"; - sout << "rEiAVG7+asI05YWpap2g7apUyGoyIdnHQ2g8QucKspJQhW5BiF4pi7Kh/zzPQPXkuxQ3biy1pOQ6"; - sout << "/6EEXB6W8dLKHTHn3cXCphbbQzqnti+yQdGuvwDiC/GWZbB7ePUsUSJ3+b7gcR54EjMeIX6rBUxF"; - sout << "2xXAs8El87oP8PCp023NStXDjpiRFCyQ9mYweS0bRh7MewCbvgDIGBuJfXL46eVRfj1sdwlnpS1w"; - sout << "aN7ePzPyTR66zIu6kwZzfv5zw10c2LC9cj/WcujPSKir/nCMFaAUroinmmNiH8C4M/CL4mU7SBkZ"; - sout << "/hidsIh9YDq7cQRP+vBwLgoVewGxnS2Q8yfZ0K9lAEsEo1Lpxg/Bhr6Ei21nnfaWcMQEVj3Gwpdb"; - sout << "fqKrvOzQrUbQBADmmksC7FSgEBUTYze282+qXvS5jnhFg23iV6bafe7LOnZmqLivJGA6S0Y2D0nN"; - sout << "HNP+0t/IutdM96z446RKbwvUrfNiPZF1CJWu+T7Gifwib9XOmMCvamYUqI+dplpbgCxmwbSnMwJM"; - sout << "yK2++dILo2W4OfcPyM9eSBgY/fWKdcv330fbSE74lTUiInCJr0hECkFD6gThcDVRY1MJY1hX2Dak"; - sout << "E+r/OKBGTM5i0AlXeZoi5+RyIiA6wRTKEzjznz3eHhAe8gRKj57DUbhXd0L7BK1ZPs/F4y3/GHtZ"; - sout << "rfh8fPWKi3TX77vcIOANXRmGtpDm3CDxlhcWq/bWxEL2u6wk4Cqqn7xxYLNmvnZHB3GF8aFfPRdW"; - sout << "iqdGfvdkoEWNNAd+OJd5dmu+kdqFXnoGtlItp5myxabhN/I5sdFxalICuAm+kAl0ocG98Q7v9YVI"; - sout << "PaajznXTdN79Fn3cFSHlgrJ7yDb5T5mzpk6ud45ux/SxLw4ERY0jykYeAYV7NQp87gSxDfp/xyJu"; - sout << "LKgD5aTz1RUrkC6VnCDdij8ZnqWx6FlSUdXt7+mDe7GAKFBHsKBUY0B7/nqnP+wS9GU7SqPuHa54"; - sout << "fxgnq2fQi/VaMA4ZTugMOvCKP6PgHWuzhFOorZKnkN/Au+/iOH4z+1NvTzBiBjCH1ZPzXEU8pXMC"; - sout << "d+XzQyzL1fd1xS5+FTOq/FV9fm5ihRm3f4qVxdJFmSnNKBdUt6+BnrDoReWLDOpNiOF9oOfADJMM"; - sout << "c55TRsb7ikJpOUhXHQsldLRMCBQOB1YRiDjLgRXnPM2Kh1/YUFV7dOS/tLUj6Th4Tmtq833I1AWD"; - sout << "CLADK2WJXlXL5s9HzGBwR4eVwzuvQsTfIFZmbnsjb+pIDR1Ek7JH3eyAEwy3WWATtUfSC0HXjEgo"; - sout << "lKpcSGD6BZsJ7IkSo7Yf/pVcQbpqOV0W0QyGcnu9IUu8q1O8iF7Nu5LNEFWBm+hpN/sUgzE+XIBn"; - sout << "3sInc0pG0ehnOT3HznAl24ljHBudSqVqoT/O/K2aswIhxOl6RHw20PypAGvTEDQThB3prX1RuTeg"; - sout << "kmQnQir2ffAp7pxEtxvQJ1kjA7Xp88ZdE+xOFX/8yvCnCWh7jzidGLOZrh6T/OlPIhd0Ipj5GiM/"; - sout << "CZqxIuPwEyKbZJ612Ny57dLmPK2J/J5AuWaX/wmMlItloLwNSvTwQ2tSiLUraT98oIVqhu/GSRs5"; - sout << "pRbWOeTspkUZVC1306DklVBAKqWSDalxyilnxToN3l1+DG+UgJtSevkNqlDpNfH9LqrcRrfmokTW"; - sout << "cymYpib926UvmWf9lsMzJfBqFg3QU5Sa/eF+BwdMgB8Cno/p7FsZ3hKytbOM5fnPzm2MYR7Kw5Mp"; - sout << "xPcptC3Al6ammAMjlp51IMU/1wyfDEZslTCZrBCGSeYBke5D9Qz31gl1Oyp+RV2lxhNngKZcBKez"; - sout << "HjNzzJHm/PvqfxyAILoth+5Cb5LxcBp49XjcuPhZ3sFIfNUs8IhjnT4RUZxJHs0PMFHwx9NwgryB"; - sout << "4UqLFn0+Xves6a9xW6mIzgSJZXxgKcWmA4MZFR1welVzD7l0Y6Rl+Wjw+qrklobDSNgntToguXwR"; - sout << "mrSM3jnAYy00HnoxJVcDFZ47Fa1aYpZvAlZHbgqJ42LrR1KvI4Qfe+cJVyV2iq3FNB+4aO6DUVkn"; - sout << "2dBbf2jmR8fNIjz4XkSCXPpwx9OZuYB2C6JjUkQ9lGfDGPfFcDNA0mjkX/FmKf67UaGkJsG+DV2m"; - sout << "sxojbh+zm48COhWz0xrBGSdj0viVa10iqcKs4+izaPgDSHMFdez7nT1aIBqf/ys792hN9h8kybET"; - sout << "GZw6HGYgD0u5+hbXeXVC11f+8aUi2vjGW9gc+R0cAnAjcLNBH6IPIhAzC3E3IJ3R37wqQ5huxyrm"; - sout << "qZ5AWYXo3PRswYjxwlXvUSwEbc+GxcFR+jirERKaqgwoVOw/2F2WHFQ5yQ5R0nI6uUhNMbTjlF9x"; - sout << "2MYa3H19/dgtY4UC496uQ0uVJ06wlVRCL1SCRpSn8y9IIvAEpkhX1b2o8NAMxR1duq4U0fUhCDo7"; - sout << "GtzXFiUKyLgKT6tEZdww8+FztcaEB5GmoIxy25ReE9Y2Yz1w/IjIvyOQvqDxqNMC64ETTqYYlF7o"; - sout << "oOGEBqO3JnTVdOtwbv18JsefEaqWs6hyUboe3zxvZqnrmSrF2Ezydw6jFhjHyTdI2rRNK7mTCg2i"; - sout << "/1fxkFC4Rlw3U4NctvjVXqhVG7/NafRynbtBxSVq17MH1Sz3IRDsKS1/HtORRuAYX5/KYcgDjNyL"; - sout << "ew/mVxTedPDQ+NDsj/5k4gjpHB28TQrYD4R8DheQ9liqV7R+shaUz98cdqoamEcpi+Vvo9eftqpo"; - sout << "/tfL8h88W/BFEBPqG5u0CrSasq88l2EWUflOfdHaVjPiUpA03DhJ6d7WEXIgKqR4KQ2QUFqhft1V"; - sout << "x6XQ5c6HvkYEA3JXB9WY7pqwhh807ucN0Kp30K87OElOIDXe7FWotG1Nu0IjnhIK93NW9W4g9CRw"; - sout << "jDvID7dpdENEY4wzLG3Q+PX6srxE/bjKkWXRrIVe4egRq1yc1BdTPd8SuxHKgVvQYGNlDHZww/Cm"; - sout << "sdtNn9gO2AN+zouZkXsG5JdkJVoGZq7SoLF1JNiBi0asFYTD3y6q6wvfonilbZSnc6V70VVmjY53"; - sout << "NUCkcTs9H8acP44DwzIAMSm+IWWynZnEmul7B9+ViqgqsHW63Q7AdBCd1O9oqYaLKa16iFiHXAGS"; - sout << "AAwYVBuirfpq/yGh2obX9w5LuIfc1Ohmm22xT1tL5Tbx1y6rOFL/LirpgtqOKACxNbxiCzEvUEpB"; - sout << "XHMNDcX/fRaVFYss25guCVEj9/YsVcDz+lf0+qb88TM8LGqYCb0A2lVHbL1O3jdvBtfw3MXHX+3f"; - sout << "UqH4kjiyAkgn/TLXzlbEjkGu9EdImol1ikOMjffpoXri7J3Rakpq19cJcXZEfN8Nl/J0IxjghnQc"; - sout << "/Ge2ls1WqEwMUrKyQfRwoJL8Pe/8JErvyUuIIIjG1PHDPAUDmEHVXWU7wDms6GPAxh5hWM9Gwghx"; - sout << "xDxn6q999jMjBmut7Xvl2yozhYpzs0KCNYzpFwSOyIjfwU/S1SvpXbV/fOlzGLqM8uDMveoDeLTG"; - sout << "5LmSt1eI2ZMnKoDquGtw9Up/Wrj191RIHkurW0RubmX95xu9KJaLOSQ07cDP/FDiME9LyrHlT1e7"; - sout << "4DkaBHyVw9fNhbeEWjF9dCOT/IQz3dQhiNizidYZgJX3d/coX3xjbBKhI18DAtQPLdx3EP6IFcPe"; - sout << "AiU6yzb6lcthTbZ+DAntjRMbEh4sCd/OtjC1HHKm0foDGYcpsN6R3pxA2fJ13yU0ZPLYSJT+3hCE"; - sout << "vyjvV1go70WzRw5TJ02F1ROusAkZnL+LVJj5cOIKQ+MNifUgd+jtwdiUhpFs4HWEnDxq2tWLNf5d"; - sout << "xQ3igiJjm1MTfsrex6ehzwmyueSZKxTBgVRQdLTcSSSiivdx0zO+/TqN/0s5WkgCufzMm6uMf/1o"; - sout << "eWPkERpbGs8c740XtOwHi/3JxDC135mV1Gfau8qPUA4VRqfTKMKwcCk2j7SYzx0RaQvaNDa94aNd"; - sout << "a8gVcQhLQcStjdUOR8smPQtqtEoIsHqLLXDZF/3gWIEjQfYjbchQcscXEi5iM4jMQCP1/Gf/oR2s"; - sout << "BY/eEjhzUNnDxyikEt+idxkKLnuRYJoRVTzfCk3Gdlc556Vz14By+uKeAmmgVVH2+U9WtTMOO7J6"; - sout << "vlpJ3lMEQ9ANonhWe788Jz9pzjzIHhx2UfmZM20I/t4U90+iTJ29/AoDc7g73bHfzugK+2WpYocd"; - sout << "ecImcUaQLdMqL8gyK882q967InpmujoXoDo4hrqpaLsowHhWiAbcPTIH6hrvkNYJ++PYo3QGCYwf"; - sout << "z7z1vc6pUEuKEx02TvYO3PX6e/i7RVBQ5HBjVsuNiAw9ai0fsc9sKjwOml7//NR/o6hdFvNnn7wE"; - sout << "Qk7pP/kasS/nVGYvsxAcqQBBXRDfDEB1mpkZX2bcNzZ/PrP5t9aS9t7LQYfqA7lWVgY+brfPTdmE"; - sout << "haXezSHgRgy8iQjBAVdBl+Y972kmR4zZuW/SiK3mkOe92GD0dVXz6l4/HL5VFflONDAHCpM8s0zR"; - sout << "HIlzwxAHl2wwiKqW2LtUIfnHA8Tb36Uix1nyKcGiYjHtv3+mc13GzYU4KZ4sj4suUs6Yr/XEtAXm"; - sout << "WYO9T8LJ3zzbxMNJZgsVHWx1UWKixJwGCv1HrEAzQNmm5aDBGYrutpNtcC8B2DKIBex+IGlpTwrJ"; - sout << "Avo0T5uJCfw3hCNwVpoSvpaOpP/hFVJYI5RLtDQbgshmFI4iFz4pIN55qCA98tYAg7a1m2g4CH0l"; - sout << "1R1ErZK5A08UpTFnxKWS4pVB1XAy7hqtEd8YTADZvNpFTkocwBFsYSsDU92hTS1GzEQ3+NPIj+pg"; - sout << "i9G8bDLaAY/bojHUktrnaYL47+BmrKQ7N9YWtFsjFrOaS39NIw6WUf4nPd5uxcoqlaM59SrOyEBT"; - sout << "r4emeEzPDzF7GJd69qFOek/DrjX87M0rDcyCOKABNJfIBaeEJKKnyniWOx+ZN4fb9meuM5bKTqr5"; - sout << "p6JLFbf7WFzqgs3h7tOcMhSvpVv4ve5Ap4yx4w8ftImJrW8f2j/OUiX6jtA9shoYI+4DOqYL1mRS"; - sout << "xYK5WKMnkftUB6lK1ldv0ogi8Z2Niil3ZBpWqhY4+tuEvTLAT8nQtX+p9/gtHxNCyGUV9nQk/T/N"; - sout << "lN/kBTdZs+KzJ0K+AIi7K1t4dVKggZ4vmqYVPj2WTKHboSzNg+oxAL8VmZAxnY2bS5AFR4wRSLgF"; - sout << "8yQj9xfyDKTPRK4D7b+Axh0s/ra4XzE0S+M2wk0cmRbb0I8/pG7yXSFzYPM0bVhRuiTVT+mrxcpA"; - sout << "qSJRKzFQ6h+KQLSYUVAspQeGnIQX70fF1/BcYviKGzwiN+T6zLH+thLJnZX/lgHwZZOtHYnCaBBc"; - sout << "t1WFi3G/Ex2+UHo+nPBCFPq9TNEWcAArNxPwPb9e2H+ZH7dNgLZjZtGQoEiEI2uwI5l7/X1++/3N"; - sout << "EWAKPziqUxX1sZeW/T9cLMiDsVajYh6wqn11+RM+lCUKvYZym15JcEKAxxsFxo3S3gbILFXSy6IL"; - sout << "AkGGUsKtITt1LB3PO3sIare5OP3YAk+PzEVZcwp80V9dHq72cSskofr8hvUkzsBZ/iH9dC1dupTF"; - sout << "UbI4YOrA0nQRnvx9AQVrPldyzgxv9y7mVakcgLLpd1YOUDS7We9Wh7exElCVuB73RtXfcd7hEjRb"; - sout << "fu0xg2KT3wOTQy7WDAnidLZLtWeKRqDLxmO/W7370N1TgXS2sKLG6lGk6UY5mEOmoLsBtu6G2bRj"; - sout << "QIZnC0q9x7sUVMyg0TOZ3698MgxWXpvNqocP7+TYdJeptTkg0RZdgON1WLlCs/yBSm9DYpN6u1ak"; - sout << "pr7GDx/Yira5gaDGvnLtoL/6L4/0bUH3UJmeNACcLec9V+qxUJnYjQPYPjGjALRfRNPV8YRsjWBn"; - sout << "eEP8OJG4i1dQAif8ehmV1Q++qHcmf5je2IQwcsrgj3+UIw6urMoRWVwDISw5Xy5Y1HwitWZ6VW2Y"; - sout << "eeeLRYBIWAsoJAw7rSyLqKjaihO8rlGw3IeVrAHt3k12wX2t2f7eLF/MKv/8JjK7d/rr7EbNKrGA"; - sout << "iyp1zQFtenTYrjr1/Wh12/lmTnipKS8ZDCJuMM0i8NTXB1fuisqNI35HCuXXoJQmc0Yfu0jA99jL"; - sout << "T87FUwkmKQA5+Q0HV2C70CfE01ALVU9ZbdP80mvHlMhiLURPjRmYjXl++FKpHU4wvRmflXG2K7KD"; - sout << "aKccgkYqc+Pw5T2AGX5LiJscIykcNaMLVtH/QYPasG8jfgVH3dENDXagJjuYDwkROZ7Dm36vvMZG"; - sout << "QkRDHXApzY0T9GizL6V9WWflM0lopSWELw4HNPExg+ZMSE1eAebW58HHTDZEnu/Yrr/itfWUid8t"; - sout << "od265bhRmkCVeEwIL6Kt7DBdDVDu268mwE/OlCRmE0Z1XHDIR8ggVbDjoQHV8Fzr9YCM/36qexHL"; - sout << "v5VxMx6PzZGBMIGuPHIiqnqaDPHhVpMGbD6Xowd50iE82GANJrgulQOlIg66T5znMtXg6g6hOqh4"; - sout << "WL4qrPm7T/2mH+T5PVIR/D+VJSoEtnRLdnO6zZnIZp7tj3jXjhNSo+YGzZmdp/H1iuSqC6GuWKue"; - sout << "agUBFs+p5Zz/yMrYI31yx1URhmsLJrIQU8llICpkRo3uqhPzpXcZs/MZIS5pWniqIfGnqUtzeusS"; - sout << "bouAVAOgf2rModIu5NWIwdnf+FEsDsw4sYDEI0jkJD8StwBgTg3uiq1jV3FP8nHWgu/QoWWXGtpx"; - sout << "u7OR7HYi+Aq4lK6sVu2qld+deujeO27r4atK+wQfmbuPyjJugTMJEdLfT2SkkW9mN2Qx4jPmABq+"; - sout << "5tCUgibAdWz5SqJBDr/jxW8WZ5YCfAhLifSbTdcM/IUF5Nq+NCT9vrcWaUak2K2uhfvoJjN7DWVW"; - sout << "6KZMzeN5XIRKMZc2ItCW7sUmAy120f3LWzGZtlX49LIY5nsawUDOrPIENNab3CWEPAuKAciOsFU7"; - sout << "CV6BvOxRekHvH/6clgwrfSCGgJsru77XV43H1/wZciyR/4QDMXPaewDGxdH1FNddnkZVAuYWrRzq"; - sout << "PF763f2SQmwM5GERk2bxwr4eeeBWBRqMCK4ZNWpdYBseupT694Rrk/bnUKjuo3kwYrs2LzRXDJVz"; - sout << "Fvj0UjorGcr63LSXNyLhyBGo/Uhn7dgtVUaYzsY5rChqvLthaBhab+268cfGyYhacN/E3vezmySL"; - sout << "J1Bj4twQ+kcbWWQE/Jx5zGkgorM3Yu1vbjilzv/UbmNb+Cul6MgnKDRFS+cGQ5UMw2DD9OyTKJZ4"; - sout << "5k+BcnLih6kE/s4UmFkeC4QRqLMBBhTlScS3wXS2lFsvMd/BXNXWtyFtk+dUP0AH5tgrRsaY2/h8"; - sout << "xTgN0sMK70yvSzojbnExkON5mDszehIEHnfBlL14eqImQRiUKlGQlP55T2AKSYBbpKFJtzy+/aXp"; - sout << "SxmkYMQcvFD+AH0oaqJFaB9JBLT7JW5D4ZwTHpMX7AS6W0LxgriTgPZnSqVgosl2DZLKBUt1zXjw"; - sout << "dbSmrROYcUAXJxMOAj6jNlv7nMkn2xV28QS7lFE+aq5yBIX+UBTF1uqk1XI6OddMKrtzh0SH9Dey"; - sout << "q2NWoOiH5YIxHHbWx2c4fiFrSCo9v2JQlk/rv0GKz5jX4GEI9+UPWzr4MA6GzJrYS9cydN/NXwY8"; - sout << "lvHx0e+zI0aiQkbZb3hNnWsjQ5q2gk2pj4yT4zCFhdL9NBTAwWrWqjSi/Zf8BU5R23WHHMMPBfEj"; - sout << "1gX3IdAZXw6tNUseFnBithp1Zo52/Rv+OUyNIC5o9whYx7T+e1Yyr2ytlKeiAHOWmiP8h+Mqnb0l"; - sout << "BXYuP1fiYcGClI5Krh/05hb4CCW0n385qHMIGDiFTw1emVktV9OTiM0Z5M8PUHDi7IGUyrJcwXyr"; - sout << "Bjh5HwdrsCA7NdLqE+8QPYnAPj+rMJWw+xrrlcPSpRBTTPTPiVnn/PXL6khN9lBiwz6Kn+X61bhO"; - sout << "8yN+OP1IWu/DZUzauOoVqhEPEARq316Uj15VXoPpy2oa9/L0Pqzna3d+8/VxnSlWWo7i7bWEXbc5"; - sout << "N9OKxooZQ6XAFihvJMIsVvkYp0oAQ4AIoOLUSm0w0ejFIWsQ/goNmLj+CQneb3+YUpdYtpLODhln"; - sout << "/4HgBsPjGnEug/fzuWbEqWXb/e+VL8RUX/r9nXe2rGeQtdQkdqsNj8avFShnnth+UNk/2TRAq9Cd"; - sout << "eDYJMWOQgP9NSecba1B8FGmBdvC6gRsp06uKfGesrktOPlzgKmZPoqsjHhrLLV58KwTavZSoIriF"; - sout << "Mzj6ZAalNMDEqUGuhtXZrhTVoRiYsy+3jjJ8IubSS/RxiQfPS3+3nudS8jzA5u4uHJcy42t04z/l"; - sout << "NkN5/kjem3F1fAu79/16XeDD/wk9A7FyjqsnyvNXsiX1yKcEC1Bw/yxCh33mgcAlh5ilP+gWc7sg"; - sout << "QQXi/dnqQebktoqyUYklPN/cYQpCQhhY+YRp1wwbLJYWYrBXiN3TbACI1koRROZw8pT8YKG4utqc"; - sout << "P+IjOrAVYg94bzGeuRVI/T+mpmGHs+lsgUY/WLx52PqnmLgBORLHZzM1sFp50ALnbYah3gAR19rz"; - sout << "twGYVQXIIhK8rewz/RwJJYwSvilMiJNx5UqJc8VFaEnwKcr2QTqlJ9FezDpuE5kvqlYl4tywsnwU"; - sout << "ta1MB0FXAC86i+4pXWfaRt5Y1g48PGEI3TdjkpiRJwEQ6YGSoDngFX5K5l+rZePE6FHNLBoRcIp1"; - sout << "5aNK7cS3MPyb1MN8zhjX400wFp208a28cBpCH0KmHvt8+ip26ejS1C1/aLvdz9j9sQtixw7JU4Jb"; - sout << "ytPOVlxKjAzFFFdaQnVEAkFCKetYCkMzrARSzcLQJcBhhChb8NQDZoQWxe2286/y9p1ImU+jHlzC"; - sout << "iTREz2ADRBGQO3JGz2h5GYjU6YjZn6ojyUyMECTZASV8RbvNy0/8yYPNONYWm8v4/LLOUM0fq8iv"; - sout << "xwag9U6L0Y+TWtW1YD13ThbTjTrg1lTHKzyp6KeGWNWGH141FgXz9+r04Qkvulcn+rGimg4SuvNJ"; - sout << "XLotrbWuhULd/esyJIAqfTtHzHZMSBoQ0WauyWV+so3c2rGDjo197k87SJkfGp+l78kqj03jh6ev"; - sout << "WyjhQZizdnuNdBpm26GnuDYmZdEaOAADCtkR9dvMWdeNsDCjyRcCbFEPiUX2xduSbw6aulm6mW4l"; - sout << "CckSOv3l0/pfw8bTKEszFqeXiPEI2SwIbSx9R1nWd62VVDrYmAkAXzdJ33QOlJm6YJT97+QuU+61"; - sout << "RVXpRanpmC4Eh3jEZKV2BWIuGPXzrDGgAHoHNhRG6vJ0Kp3RqaxFv3cPrLZdxDnQhZQH01O/dZ8S"; - sout << "swcpb3/D5GdWvUS6MsIPhO+nhoabkyxwvbBNzyaLVDoXZK6taVT2GWFyYqguu3ubVBGd0H0Mksh9"; - sout << "luAh4ogG6drswOFxJhR4S8VIMkF3x8PDUj5w8WTC2nlhxIYVh1fxvdj8fEfIH7WzBElS8vpZ1tXC"; - sout << "1JSgHngiEYafDFX4OJeHxKFu994g18YvLkJDdQKzTDvOiD9TaKnnaSGGXZuasKTtD9z87GX4O84P"; - sout << "rye5DvbYUP3bZx8GOmhf5YsaLI6b5m+iX0Mm8ulB0hOla8uE6EfLBovorLCKke5iPFU/gynxeCkR"; - sout << "8RL9zw58ATEm3YuzE6nDNaIekqV7Q0MqCpnpAdBPxjtEGo9yRRPeu8SSSGL7IGKUUcr/6Xp8ooyK"; - sout << "iK4f+mB0hgO0pQqffQHkEXxGg7Zd/eTGAO/n3Acq6bjGr03T6LEO0KFJl1/m96ZEBQK/iiV4m5+V"; - sout << "bZvr6xOrErUE+ih4g6vk30cSQqFvyH574K93bx/uyydqzyPEQTZ0oJmT/KoUrYti7CxlGwrqPDTZ"; - sout << "AuEOwQGqqxoW097Ql1bpxekOevJPSL21snAT+Lf7JZ/79YwZu2WZnWoDQRNNm4nwn5M6IPmErESJ"; - sout << "szsF5VmUs71cj2/gnK7NDU4c1kLjXSsVVnN3/VL1mOnYV25Nh/ktPRfTLn1TDNG+rkdJDjGcjjSd"; - sout << "JY9Ro1Rab18UKm9nE2tWjgbNUQbzsisy9P4F7cBLBd5K6y47wRYZ7MamnAUZMUEDmIiO1SR1uLey"; - sout << "gugfd3Sb93PYjImZCjulVD/w64IThgqDIgVd1BOdAN23laLSQpfuR0xW5k6GqGtiP98VwULbV9rJ"; - sout << "thalp5t6mwsOdo46101N512/p70XcZuX/VyFxw3bGO597RbO2nj+gM4UanMOfTEQch/kaJsk/WTu"; - sout << "m4QMSxd9SAn9/aRXKR55Im98Fx/m9q2GB0vn8RZqfqHAqjmO6F3usDuUFxk0kn5Hq+8jXBephw4/"; - sout << "YCOP8eBKnY9V2k2bnQO/9BWhCyuwun8huNz6keaC+qa9PFFwXmAf54BIYdgr+NspegaRej7bOISM"; - sout << "WpHZ77YuKL04uozXUz6B+0Jy/zDzI9EgLTCk9L1CUpGPSoNGpDfb9VqkOdTvjQphSOPB5PxD7NNC"; - sout << "tV4tl1nhB46JCCEalhhsip0/ZEtx0Hd+UNxYRA+qjaGiEXDPPDyjbYYHWNRhYKo1SDJLVZG8hoVh"; - sout << "stl5hebAI8t8rCBUF3x1HrMRdrRX/GEzLfx8xZgk/YAegRw68TfN9V1bY63aRS712i/twY6KDxD0"; - sout << "V0DeubXqkK+N1ERAn8ygvQ7EJNvGbp0OTu765R4zv8y9u+GqdmpA5E1Ti6+2l/L6008zbYQVWG6z"; - sout << "ejkRAp4UjMSDTf2zTNjP76zzQQOQwsmwodggF1paT6UlXpb5HHp9osSrdwGXb06FiXf2uVo1rtSh"; - sout << "Stzfee1i+BebVOqKM38qGjBhXwGiiiZh/mwuG4Fa4wOKlrqaTVEHgGm5N0uxr9by1E78BXj7Nha7"; - sout << "XuDtqivJf0brsiAUNNtfUjBT5WJrj+iYT16Zs7ds6Fm60mdF7vSBrxXPNw7ZuueSA5xuN2bkeP/m"; - sout << "CQ/3N3lGgTQlMNkMUzgY37ln3xYCXGcMpQ67ACMf4gf3l6FhI0S+JMwzxD4XMZBTxgt/2YX410Ia"; - sout << "ZijtokULoOBQRqLjxREcBBhm+tpSSU5cRb071EVhz4K+aSN7PfcFOTMC1mVGy4cibxeRIq5Un1o0"; - sout << "NYSkHLiVCA4OCLkemWvzxswd9Fduc9mK11xyqBPu688yHH9y2e4Fo5YjiKiZynd/kydWuNsYM9i0"; - sout << "U9ukHpMFmrDmIPZJMGDgZm0I2QPvoieRcpitQwNYh5x3cvsEs3eAAc4mfxQZUR+v0gClhfu7wSn2"; - sout << "XBPPNVyzE16RK8q96Z+w1paaFKHR2t4TRGhmeCcxR3vS+jvqpGaYFJ+YJe8hvJYK0IMZTBSPg6Qx"; - sout << "Cx/cwlr7gStMvrFBKjLGDMAsrw0OyDbIe6/E3Erq43GHsAplYSxYAvOFZUwX0b6VkfAB8CsBnRFb"; - sout << "jnZmZP+cZPIySLLGJJm/oIs/Hy1wYuyn2XpI/uCuBDZifE6fnNzdpzQ0BpH3qjaIF4OV4gghhruD"; - sout << "toj7yNaS1FZl8IhQ59Z7P1eSvQNulTHYsfBw6E8RE780AO+aRHAxVtuS08RWGrde4wV3ma1NzMaT"; - sout << "6fHsCr9IYY1qACpBtyGz389+cnXpeN2VZczNw7JBx3VthteVNdpX7cpftA0e/mazMuKLX0vcBfus"; - sout << "/p3Vj1SxPevb5pslgkfBTVcFxmEM9Doy5dSKQ32SVFgYwTppEuyviNKuUzQWwO/XhsL50gcuUhyU"; - sout << "ELpBLIkpABrbGRa9K9ye5qLWYYzzBzlPoyhEwt8cWi4hVnafg+RHEJ7lXXQ5GlEMlzxPJw18LwZs"; - sout << "jjbC/PE4abuILrfHlOygD1CqQeYzzBN+gwwaTbmquBtZyIMvGr5FrRqz6sRP/W2OvOBN5iVSUa50"; - sout << "VjKdnNUwSkeLCU14Lp1zN0WQQVVNeR3o1JfJsGvQNjD74MnSlHTFSUYloSFgHbM2ANeKgiYN3uB+"; - sout << "xLb+hRJPxIwiTshZyclnLmv5mh63OWU+7afdA07BMdmnK77uFB1rdXFJIWUBiUG8u2yqUk3PKVg6"; - sout << "9rJYvVu3eA7mjBqRvL//koo7VJt6g1Hj+8lnqu71OwsodU/t+W4tmcIKP9S0eafHEQC6hYCu8b5v"; - sout << "QgVuWf1LnTXwTGbm1ohar6OTxyZV07nJGC0xscQ2yzhxnssaCoAw5xFUzFrgo8a0J1Nj9aOaV40C"; - sout << "3y5lP6NgiO+3r2J8wUcKWEVFLHDAYMXiMfe3XMp9/ERswAGSkzfXSxZN8T4RPorh+pjPH7sZJExm"; - sout << "o4ZOdqaWLdLS03+0kRuPXm6FvZCaMY7Hz0e2Zv9X22KYmXluGqcgErFTjYWhQFhcpt8oZA5T3mU5"; - sout << "NDE3qI+bgtkN9vvNbhIrrOH7mKm0af/zrHA8nnU33sc6Gnv0wfCvolpiyTqla+HU7rwapX0+Ge6j"; - sout << "SZZht4l8OPe3/mSmwGjX9U8xWxiIhhr9VbVxZZ9UTnAhfcceB3TrDA7F2SxGmfBSRvQ50uRp9Yi4"; - sout << "14Sasx4kgF6e2wJh7TBRfDMQM6dew3rmIn1p7l8f43AcBe7T3rnWB6UJ5eh14+xb6HwyT260Njvy"; - sout << "vF8Z4nTq/1MCLv3c4lQoO8yuhFA+J3rWlYTQmc2ilfWbYpdiAOxT7k44QYoHTZDS58anIywl9J8Z"; - sout << "uBYa+giIPDINPDLGNobNEPl3kerfygAFfVkxd33eO9LwDAMGAZmbywt/OwRmraCFhT8Qz9jygFse"; - sout << "mT1tZ7+hF2rfTvyF4OiTz2nutfzVvYQ45DLjmbHFQsHMVGzU6xdLQlZYWq+h5tfVng4zhwIge8tB"; - sout << "mA9ndgrTZx5tspm45fVKTjE3MYL5qNsmzghyfasarz1MOD/2wml6uzWZjtljHIzap5gtIchaIF3f"; - sout << "JMOvFu8Y4R0Fu2UNkbOfy/YLRnKlUHEChwTeEavSkaHBkprvRZJuMsIWcvEZHDIWdRte0qfNm9V9"; - sout << "zWmj4B3RchqvXh5XsoDkMqergDisI4gCiHFIxzzfJBsX8TQDUQIvAGk0pc3ib1/6xwCvLs+NXC+8"; - sout << "0sI9e/aruBe9uEXGya5n6Vqy8z9soOoBCcFqc4B+1ZUOyN2zpIxVFd9/20NKmp1W8RjRforBvF7F"; - sout << "2E4IkWh/3RcL8tNyPpWkAA7Snlm8oO8L5cc+lMx0ph7DzYX5NcIc6V7otI+Bd7w8xLSVxzfX3Awu"; - sout << "S6bjrU2af39KbPFq2LpGOakR+1gAh15sd6WYscozdkSZu7kGpuEppnuMxYFmbMrCxtw2lDt4IwMY"; - sout << "ttBHEP2XwF5z6DQpRqsWBMcuBbztZ9BNa3y2aR8pASldncrnRjYQ1Iq6KGrHeau/uq80ui3VPbOD"; - sout << "IrV/JeNFGet12rX0zneHBGYUxG+Pbso07l1fJxd6OgaIrwVksovd1psBmD6qYsbc3V3dJLacNOdt"; - sout << "fvFnHWz6EDWivzMDWvumzAO1JPVFB1TebaaWaINSxaxS3h3IKUVPTu+2Ytw4Plwt6fc/LakedkFH"; - sout << "fxE4uN3LR8Y1bKQN+/VvTHXGUtf+UyZx/VWHaxuiqoxXUazpT+l2muie+/2l6v8JUCBTUYwa+2W7"; - sout << "WWvrBxyliD2V+KaN4dybWey+3RIp68UgV0gP8h579kezx0+Q2Ku60vjCIKJ4vJB8Gt06a15lHOmx"; - sout << "WcJq2DmQuyWTXsqaJPuljuHUhqYWMPnqY9gi53ddw9SwcpgE8cCC0cG65FKn23C1Ihh5NnuBohYp"; - sout << "ly/QVOFi/rGW9kKrF9FW7C93p2DxgBhygKC5E582EIjRXm15bQ5bUSwxfYfQ8VDCPlL1cR4yhpqf"; - sout << "CpAd5/ls0MJ3SFgUS4e96011A5intr3a3UMPqdMKWrwCW6cP+7TJKC22+LwvAvyS2dI+NdyLt0qY"; - sout << "o2t9/ml4u0bbBjwl24o/CIvm7T+045fz+3h4yrKYYPSbHPgGiN0+oTcGhtjBgtil6WCBMuzgEn4Y"; - sout << "zVaTD4gQV2l6Cnpi6fi3ZIOwuEi8aqfRdNrPc8+Rtik2wWdmbYZ66d52HweeALKwfzIm1DESb/Dd"; - sout << "qc+pz+yVNmYp5fffsjf70WrDyGWMDqSVvj3tXMkxKiKRetutfs0Tf3vGDdUEdTmtw4rEca/1K8p1"; - sout << "eNpotuLSio7xp4/gz4cmq3IUEXzk7b++TAs6GAq5ddjbcTJoyRSnOhu2oetJGzWJSglrWwGpAAoC"; - sout << "l5jHFfMXIyi4lggUHjIcpEosLLxntEai5cP9bcGzTw3bnnQMo2mI8bCKSrw3uzKapI6MdDJ7fXZD"; - sout << "aR+zuSlVjO98VU+BugUHWJcrHneh7tRZ5ijux8t2d6y/iGeJm0gs2C6ukA9PTwSV7UxnmVOCsGqp"; - sout << "kh37fINlLQWIjS+fVQZwYcm5XyzPzsyHa/x1DZ9CJgSaLrOCm8PDPk2BHFSdoYMrUGn9CSL6zRNN"; - sout << "TL8KprKSZLrhrk4wtgRrTAkO9twMovO8Pjq/xgO6djfJygflU/Yn3VPYjg31O0SQLXGQLTL2SMcf"; - sout << "NlIqmLEov/m6A5cs5pJF7be1yX0XneDEa7arl7v4tcvMw9Ot57OytQUTbFu2iUb6zviTVWMEMJST"; - sout << "eLj7c/+RNy+lhBjlxG756YIjtWULrrdPBkie+AYHMSJ7PnJ/QAl3D126W7D9ubCjagGVZ2cLX/ft"; - sout << "/pLcIuPRcRUeL/jTGshsfijbq8us89JPjLS08HMiaaPhTtSOKbyqHwVEvfu++thQZsu/3wM8f2a8"; - sout << "CnKdaRAkLethTUyv2xNyQmx36Qg3LVGf5W/tzC9U2AQ1XRE5l/IoL1AJ08JdQDqHSAEAiw0LWMIW"; - sout << "a2alrr54YOp0qiPcRTLKo0gZLr6Sy4jKmQqEFrPCxqPSGMe2pc/pzVuxlYhW9fDMjE1vvfzmWnhU"; - sout << "SSTXpxONjtMDtFWol0pYyjOLyJcATXCrOK3PXPX50e+S2Xjl1iJezlgtdqWVR6mK8yJ63GyAtjzT"; - sout << "oBIBQjydPb9tpuVY3Qy129f/Pnw/MT/6wggYCZDuhhNmOqmSRbm3runLfwSX3G8XbN8LEw94wy+n"; - sout << "yMs17FWzTwWk2fMEIV88TuqsxDPW0Ko+a+OuiOQ+7TGVdOLxtqmUDW/3n5QA6Ryc2kbaFhZAhONU"; - sout << "zJXyJzll6UPZfUsmAiX3u/qyTWlbB3EKngamgmD/1WNGX6ysD3RYPu2olopo9wOqihwweOtxfQYr"; - sout << "QdXhUKreENZAZ/lOjn0tSfAFsB21/MuDNpuz37dI0ACfvKyPdGnFpNocIXHWmSWZbOB6OypSbzMP"; - sout << "5r1fNPRk9qMY01lKWRz+lkFGzl1vbfKfSO17q073cCPouHosT+RpE5HBBf6E0yG84m5o3u1bLUBb"; - sout << "8V4YyMREOIa/2wkUH99l3YksFc3eeUMzHSIoHrvCQq1CTyaed3tNbZw0rHPLzwSSgQMZAhsi5mxp"; - sout << "ZnMABMsXSHdvgBRkVn6PH5rjrKAcSnikddL8F9/FXvJR21nKfo78g6RULWdlcc8tPQR0E+PyOoLW"; - sout << "VR3yw7L8Ddl1zHu7hoerz+cM4gW+67kjehlzV+BAA70h43CfxsmvZm0Kaw9GOkxjDs8F5b5XRtdN"; - sout << "gxh9Y5o/vlxI8f1w388jL7od20NPAYSqvONaJjF4nthDN+CqlNWjy3K44Xeghk1o4smFlbJM1ZdM"; - sout << "taj+ZlPozWzYEugvDe7/9woqdFtYqCWDlIAcMGqw6QGFGE9EVwP25LeGWnM4bcSWkdP/Fc+Bpssq"; - sout << "QaRpCt/+Igw9C26fM3zSM3y+RfDs6dseYokawF7xp7oo++opFuMO18Flo9dJw9HeXMZ6R8lXrNrD"; - sout << "l1RWaWakmYp2KgPJYT9USAV6S0JI0m9MxL0R0t7XT23cWgtQsG1m/gLb7HT+CFNm4SYnWMGpza7N"; - sout << "4w6hXTItZLuRrC3pH6AF/m8MuwdlKJEkTn0Mmu0e+MVe/DJmnA2w46oxML03wNy4qZMj0LywOnIu"; - sout << "Ip8p1oLIviccDGpnXqZORZGVMqzuidIfNBSgalxT1A+ySEAVroXtGjtW/L7sedcTElNyK+aspa3l"; - sout << "O10XSF3gjgcZnN7W1j3wiqePmtzL/RlZj81ruAoYJRWaNUDT28p67/k75Uhl4Sh7qWwc/mJnG9qm"; - sout << "rvZSX5GiW11EJs35JJ08G3UHAUa98VEIOesPMaL1Wr0CVHFVC9eW2iHwEq6vk3JrGfsKARrZgg9B"; - sout << "YDhkyO+3c+qNgTk1WMC3MXaSZg+mwqwx0TGeJRg6/+rcKr0Y+y0ZEDLz3kMD/aihZ0ictCZlcYAz"; - sout << "8pqdirBd1Dbi8AaMCJTWHGWaOIxmrdU2XyVWVk5Vpc0rmzyc3ya8WfXjkdDzXKUS1IIFi34b3/7G"; - sout << "qYpJNqtVAPT1TXnnM+2koRgGaCARu/RoKzl169YpDYPHVJuRJDjy6lfrKNEqPzJY9phbh6WhmZ4X"; - sout << "CZMPrgnrs+0Bs///61zNUJA3fArNyessRIkZqSxnKjVOHz6/LUB7/UzJXAd/XfMYi4yNw72BSAjJ"; - sout << "Z2AdnjGIO9p0E2KIOVx39bETt8l8JKOyT4JbimwFT0IN1AcvwH4wgwtKYAJ29L0kSNoBmMwaKCAj"; - sout << "1I1b8usj6EM/OUjn+e9xmLMkeAUo8HM9BvLP33TuKJwLINhNcggbZVeaFSwBsvj6hEQ8dN06eLJ/"; - sout << "yIGaRsni2n6oXeRUo8/TVWmNIbloEE7mQUuttR1pVBdkbJsHGWm/S5hwEmsKKOpvQo44Rao7fsDK"; - sout << "cCtKATplclwuCDCPIogRMkpHRSSrHUKE4tzdA7rWWj/jaYc1Sw6hG7OaDFSWuP09Qq7klmC3F+0U"; - sout << "CIAtQbKJevYhCSdWHfo4THHFR5CeuA+shfbdWyPNmP1HRA3pvo52jSsJi8HtPBp6XmGmM8dN8Vaz"; - sout << "pG5d28HH76fj2Ny9McHrxGFaV/WhhoST0GDqN4PnqV1X6d05aBKfwRbqdWo2XvmrC2YNI7Ou6+3M"; - sout << "Ovp0NH7MHXpLY0B+GMf+x2X8hoXkffnGCa5CQ1SwTbZRkeGjejwGhpOybsOl7AaSA/fqymEW9JMF"; - sout << "1E9XEtZtzyATkj0cBUlofwC6N3N//altqBAbmDKrHThV3SDsvn+tpPiZG4+pQQOWqQomy3QAWwVV"; - sout << "nZTorQIvoPMUdJWPwsVLaRldOrUEjmK0OF36+QysxIJLgj9x5uQDymixyS0Pvu6ybdB5Z+ggH+hs"; - sout << "A4wt9pW2wbaOGU6jjYN42C/ehSOClqGP9JE1qejqcLRAXm+JDDFZGmCBfZRw7W6R39cNfrbbACVF"; - sout << "de9yicnqvNbpaeAWAmfjrwg0QvVVmmlvsgDinO2OdZ+TLx0KKj+4+Zrt0fFfXQgUa2oIFGoeCuQv"; - sout << "eyjv6ZUnkYHtJLeMDfuhaxaV6dIG7US6tdGcxKxw0/Z3Cl1AqyDSOSeZIfABudI/tNrQF3bk/x3g"; - sout << "Kb/i4VNKLwEq6O1T8HrZSJzTWus7XcQncGAOJWTsuC0/0Cv/Eg7pY87c6VtF2/kltcQeH0qWNix8"; - sout << "qHpkEC5Nmjm2erJhQ2gsdVQFrVjxv+irSaO7GLhuVwFgj0pMzKDQTFvd49DRMyrwb9cml+u9MZgs"; - sout << "Uqwmc0zXzCCT51uF3LMqVbeDEgt/Q59+03U1N2QAJXtTScjJO97PYZSJvMEPONjSI5AeRlRqpD23"; - sout << "4O5xrQGkzb31WrZP54ZyZ7Gt4T4bxdwVIZSrgSrGJt8RjREWl6LuDpuRsPgnt85EKNRjf+icgibb"; - sout << "sGxnWWdqFdOLHdPKDyeCTiJb/5dc9YvjVxWINlgOMxdYpUU1OZ1hX3OmDDepo9HP5OWA4GnGXR8Q"; - sout << "cNS85y2Q+DS7RF+qxSbIO7hOcNL2PyFxScBXYyrkLUWWxzfc0BnyCaCg4lrMr6uH8vnM+7u16IsY"; - sout << "0f/y4nxYNholE4Q9JlbhXVTBL/ZZaC4qU+/mXrEBkUVY/xj8NqYSicBJpv2c0IYwtf6hwOFdMm8H"; - sout << "y+l26vbWkXEGS9XV/dKKKLbW8EqhKxtlRGB+Tle1HnOp7HhcINVwH97RL9vS8EurSNlSWJCHc/1P"; - sout << "Ui+ZsmkNIQtV6tATb57g/AaResCtNWa7znBxzJUW4stpxdxjD7fLZxyP1dSAlXDluOaJ3vaUpdnt"; - sout << "JqkG/gHcvI/0DK84ddN4VLKIZ0XTrCxvTQJLJcUhA0ENgzdIPQj96CN1XvJ/DdFxx4YKBnb5qyA3"; - sout << "51z6P84v5wm+ciOVpTFERffXwtPPfro5z7/ZWazbUjB4AcOFxj5Sk4EweqTrVSD2Ujv3quBT8SUj"; - sout << "A8Mc7LBMtANusOZeMURs5vqoI5O5hwicxsnqHJeGe7FMRRVH9lrM/+3W5dfm+jJ/ntoZJArHCrWQ"; - sout << "wJqFWJksHTraWTJ8crxkGSgZzFhSHv5pkz/FfVPRW91XprikZvTlVhgyTFbB5gVGeEu1X+M1m+29"; - sout << "8Gqo/AYNTsXS6KfOWnfc+jfQRkkjiLsBS2vsnV255hPBz7Fvr0mqaN6AawiESUSXcFBnfzWQbOdN"; - sout << "wzA468JTsVoOz1MUXZ71hOcXpGMVdKN5ABH+8imVfDYGVUUTXv1e1Z41jrzXVphOQcQA+bJYc3mP"; - sout << "K0HVQQfyuwo73PTJ1pINwbi4OrAJvTP1vEuo9xMtyc2d9zIN5lVg0gTlEE7uwGWVctS67onu5vd9"; - sout << "cQ4/+1axVD2yy/Z+s9JGRZ78DA8rIUqVhOTWNlqmqquxf/mh+nY9Q3PdHfcpNoqc4g465Xe3CvO1"; - sout << "cBGJT0vnZw1vV7rtQ2FqAB1LPAp0f8nIT3VExu/mABWauAImnDE7ZiEnOuiuq2vdS8WspLMjktCg"; - sout << "vlvwF+7AGxfwDCImKSzh4Ph5AA1VuYW3sVQ5I3XuONfappCoJjZwiV8jyKC91rHY1/jP4eMb5vwy"; - sout << "8T5ciRyzv977mW5MK5sx3ujnlVFWr8y8VDhp4qmhnvvswLIKBRbrmW7jkG8Jg1YB7mjY0rP0sVJl"; - sout << "zICdb33DVPWxgRHDjyO189HiKWkzbH7JTQfKOBQwufTNQ4v94HlyUYNTzHg9S6nqI7LiBq8zO784"; - sout << "hGYDRvfB37TziuwR5oHeQ0IiV3x79V0+qLUHMY7ZFpQB+LnGcxG41kvposK0UBUCPDLKsLNQDSn3"; - sout << "ltl6K1uTz/4gjdLjNVlkpO05xJXjK4JEoG+siPuABINp/7iLAgYGHq2G9p8KmKiu2CLtN/g5Aa37"; - sout << "pewz9BkOuvCHm+J9/V8+rMHTCxD3QT962/XOO+iguzglwO+YlN3BpWTnr0VhpQN4ZZZcZcucEze4"; - sout << "PgoLS6CcGSP6xrMdnasvqM1kFCvgOn2ROITQEJzaakPDaB7fzVIw6WyusM85BSNnyc8XT2RkDJaf"; - sout << "VTGEMba9qIAi36u+pC/PH6XTEW+x6nehFJu8q8e7B+VrnN9lkpvVHVDxjci3CHvG1QLPGl1GaGYJ"; - sout << "oLeINwB1VTKtRtEyKENgc0MS2FMk2G81MoKGKVKId2oddu9ACvHqzQMbOqnzp0l7RJewHez830/f"; - sout << "8oKpsUvl1N/b9wfTXJwN77MF/n2zAg3VSv4HInhkdGA9HRSg1JylghqBAOrqpRsh6r/TG6ByJ1uQ"; - sout << "rv0aYvVdVaHQ0kHH+ma5Mlc73U0x+ncDjlicbrIca7x6y/b90emfyRcN0uEKr0fZWtcbK0+1XJXX"; - sout << "WWMGGDUO5b5cJOpDzW7GJe6i1m6yIPPlfp4AWLa4M6F/ys1ONLrbXqjYCFJY4y62OeRK32Ff2zN2"; - sout << "/Lw3hMt6iPTm7oiJOJjDcamybQlWOoYIv8tkEQYN2nCX7k7DD6z183yX7KtJF8Sj95nOeYpzAnLG"; - sout << "YOfY00SA1cM4aLRUPG+FYrzn8UFF8Bl9UFrLXoJ/IxXOWKqrbjhsKxMvC4xriww6KZqHx0L5FWGO"; - sout << "lcFkhdpx2fFeswyfjQwtyqsGsiY7MXGsWA7fA1TOv+FZhJbgCixrx1rxwJaNel8uMo6pGHsZt1N5"; - sout << "Cr4CkiA5ZZ0ITa63oOGM0tD30yaSLkSDZahPT7xo5s33HNAgXNxZmYnv8M6p421yuNPraabZyJ5P"; - sout << "3FtNwzPpw7Q4BPSEBoKc/UbY+mdzm8ES0nKPI6Oeg1tzc9NgP5JaNVgWymA408iLfTjxaxS7FqOw"; - sout << "BCYBbz0EdJigWmF73di27QmfsygTWbHrfdqxCafzsOPuk7l+W26tfJTVxP7S5snKfhbqVNrV/4M3"; - sout << "uhpZthruy0mSHtBx5CuXAtGNQoesk06k22scTmX89BgNsYJAshikS3IHg+LMMfaxBe8KQaaySUX9"; - sout << "j/ULsdT1YZKTeV3NVn/Z01BxF/ZPvV33Dbj4bjEcf2N9byWwicz2CFpkFyQlXaSPPSzMYYtSPCZH"; - sout << "QqXL7ZKh1Xkgh68h5WyuoKkzDXGuZcl0bGVWXSOfO6gK1Mz22to6vJ3lfssNBUV1mxA7Yos1bUb+"; - sout << "tGBxyzoBmphWy8dFCdE5vN/WS+w7VV6O/JQ6j8qbpBn596kNhL4iaK9Lzh0yXRR85nqJbi8bkIZZ"; - sout << "FWskXD/A31jO2IUZKCdB/iqtIuEFZQnq4LLgCTlwF3GbxPeZTlJ1yKcy/2e7nIHZ7xHeG5tzyqjP"; - sout << "oi2AAttJbrL9n4Ebk20aqzYxdYUkR8DcflVhCjPDfeczGwzwJxwznHHhEV/OuxZpWYRvTQDOuUzJ"; - sout << "Qpu+291rSMqntqfrUo7uxCGJLmyRwWqfesAluH9y3GQco+V9yYzqf7ekdNpuygq+htLNc2lG6deM"; - sout << "02oxJHjYyjDlG48zlMEFy9OhLLLPvFVmQWVL/D4FwAwKSQfYWmkUbgqgB0QwHBFSA+xqZBWZbzoC"; - sout << "3xLt42o0hhdlZkXEQtGhtaqMfU4rBbKWxKx1gKhHYk/Gzba+PSi3J0e1A0fC4chtUfWc9UTZT0HO"; - sout << "9QLCXzuC90KrYTYOeaPSoxfOm2aboXBWQfyxZGX9fKRdLgfJ9+8EHw7rD2TUTJueSdfvVahIRi+t"; - sout << "eiEAG3CWoToa3S7i3tpR+YczkNVTl9DCuwD/QMAuPo2mul4bdVQt2NVnoz7PKMWim/JYSFPlJBXP"; - sout << "RaKg8manuhMoDfHTVx3FcToGnIDBd2in/rfbEjE46d+98FRs8p/zhmyQ6/+xw34DKX/NrnU42qli"; - sout << "1+erGDbJFhLmIoU3OhYTiABG5JVefsXQFgIL4kSF+Scratkn+s1aW28PLoooaClnGF14imxCA1Dc"; - sout << "Y/O0HqBFR5lAn+57fsKP195CJBvmyPR+UE8pBE818w09TvBmN6onwBCFtsrEnzTmstINfz0TX5lP"; - sout << "7eOD6+XiZM3y+5YBEK140cgAUae91wD0Hjz8chAlXmgE8xZfalAjHnS1c7ROuPxV9zhj5AjnXV/Q"; - sout << "7Bllgaqoyl3q8Sc6eXgEpJMz8fyZ+LfMkKkMBycdjKG0AZAkF5902y9b1VPVIhgIHueHDt5irhQE"; - sout << "2slLN6pSkRg/3FxPb97BOt0/zt96EvehPVGoKs7OlOd5C6tQLWio1Eh9IL67ldEsXOiwaSMw8QLs"; - sout << "wAw3Vx1VfWnDCReIT7/yxZGHmEGhjdtqs+gGPtZ5jkIp0l4QPQwLhVL1tC4RSWlt8BR2Nq0Ivon0"; - sout << "E4t8jhH5zWeLYZxLIVkyW0SKj5xi7s4Xo2XOZL95m43Kv2zrxzNngy1oPOXTAEUaEwuRKmo90B5k"; - sout << "xdxVS3x9BarxlfUKwNBvwQ8OVmJu6VPWQ5hx++9ZLDfwK63V0gH0tIU69q3diMOjXv+fmvQe28cx"; - sout << "iUh9o3Em/w+7P3DTZu35eSPUD6yzP67VH3pDvahh4uLzes3bdTYmpo39kD+gFc7ADodPWp2E4787"; - sout << "JZaS2A+efTMt/OPen2TV+wMSSK80nbAta3VE1KSQogWp2bFPi0NXqC6GZ+bHV1DVnw5YGs28V5Zd"; - sout << "RuuIzTLauK958DBeAc57QbcAun5sEsPtg+CICI1pcOGhv8hdLzT+YKcuUhji95EAYzCj41nxMM1b"; - sout << "wP1rH0TG7jaHvET8IY4dQb5VvzNXVne0FExHIyIFGIUWoyQgjROX+cHDLe39tvjZo2xs27/CAhZH"; - sout << "dsrgamGD6wj8ZMH+8gHzu6b1yy2crBAw1Ot2JdNhW0kp4DNdFgjqMcZVCfDM8qRZ71DBO82U9QU9"; - sout << "hXh0DCIAyryS/6W9mY2I4IxPrbSL4RkmKzhepcIoui74zu3p+lKY9LgUxYbGNOK46S2olWwJYf7Y"; - sout << "qkOPWcN6yen8rO/9Wkvmxl06qSDDhcYdsc5S409X/VeWz67rJ2vNDiyTKjUJaDWcSU/znMnhtosm"; - sout << "QT5LyX0ZVd++zQvXfjSjBLkJrki5vvKMZSUKhiYL3mIapZpcnsOQ1WRwnmSEave/MidONmZEP4rH"; - sout << "OgS00fnCK7eAWXsZb/rk7tq0cjAFsNeJsHe53E2hJK4TyaYcUi1IM7ZNSQ1yqMYbcFn5bEHBpuxS"; - sout << "+TUy1QppziHZ9xoLn8nOxRstSGxrhBG3WjfMhOwR6JNOe4Y1RDfxVHYeo2BAmtKUutztKsf04UNy"; - sout << "XTP5HSaKx1tZVMDn2fkIj6W+s07imc8sg68Sd1lwJx09bG9ggOw6D0eouy/IeJi9YQAyZT2BqBQ7"; - sout << "2D5CrrD6hqhVF4IVhBPCkJ8+9hbFs5EA8XhItGVBPyXg4/alIJq2ZeBsjhK+tuKMunwOl13sxzn7"; - sout << "iSO4+wO3geQ3+SzKShzywLk+uGm2BdS6teWxatVdm5hKNDirghiEzqdHdwGSoZUlLMYp3KTdzpMg"; - sout << "eAAPELcLl78jObmQkun0ECGaV0paIRhytIRpV3yJGmrRIxWNI5WrqLtocN7hnm4fTrzdlSGnDojf"; - sout << "CBmISIwp60UVjJijLDTVYBIP3VcwaoT9V2F2HkDZ7ifQUTvHmxRDFHuquws0bNRjLSBSPa4jh6th"; - sout << "6h62qlgbHQ/BBLIUXi/I12CwYwlg3ute/qQJIpTCsRxf4rgtEjvV2dG9ltlo8PPMhOd9fkqQcvua"; - sout << "BJaZYLYw9rxq4U4TY/IxFQe8MGJ6It9BOFHD83v3awKPKP84inEWXAkFyI0YYmzFjW/gofECo69R"; - sout << "Wk4/2wv+K5MFEEIzBUGsyrjz0TmiKvzEJ08KZOuWtFXeF2LUGvYeL3TO8FyLRpPnCAoErjZzVAhI"; - sout << "9Sd3ukmv2oZ5oYk52HrUQZ9WywqVgFL8r1KKEouCd3iNhmb+2qUSh8VcvAncWTQbn8yjpGe+kwUf"; - sout << "HssRskQ0nKLIvCz6JwWEjoaglBC3PiJ0EOwDLNP1fKXqPtcPglnu6aGaTrb/lOTCX70yvk2XpdPE"; - sout << "HzsxZHKsV2LxqDI/IT8yIxvM67v8b9CSGOv64a8MSBZ5zYOQR0hunA/iYWfjzuvX5dgW90GnM5ZR"; - sout << "VWoq9zxcBEr+Nk0HlWHpPm3o08OPGmfx/ndMbLLtuH85q4/7Kelm3NSXLXfOxjkRuSZkcAeiRk8e"; - sout << "64X1nXADtf6z1Uaj8H0JfREe91GPSUfyZjwI0pQLDnnsgGoG0DGn01qshIL3ANXsLTHDsdgjaFGP"; - sout << "zZ2f/7V/NKLrJ6HrVpWoQiwhNGY5OYv/KXa2q2XMGybUaoONCdnjGKzPYyXA6C0utQNy7JTKcbgV"; - sout << "/71QilRGXbV8OPBFLLN1VNHV2rQJDSH6UL4RYDdhRSsItpsl2tMJGGS2RLH/FwEv0jDo9e/218Fd"; - sout << "0P3DIy3wAevcgLQu6Ex2SFlTpZcvO+yidCgCv56rJHlVxgN7eSbcKS/l7CiAcafTsjsNm5VtgTx0"; - sout << "CM3ehbIeuPrV5GqbZPgbF1akOkP9vzeNsGGW5kep471Q+xn5o78MZBz7YsMT3IAHd6RViNn/Rrbj"; - sout << "gLWyleDnUQ13gY1xNXl1SD8xaQVvWvvpmlZKyB7uIPEL2VrDZSxmFYyhdhK64yb4Nvvawge8jIWM"; - sout << "a43Sgazodlobk0CkFktZz10gZEUobBos3idIWk0hQP+nGOLhiGO//fwpiplh+g6NBMLj6vI3GoHZ"; - sout << "MKoqqYh4LPDaEtnvILpMCP5NJoBbxNLrUALf17ObB5Kgz9YQsxRIhtrXoyK0XdU98ws4zxDGn5Od"; - sout << "Mj4Zpw/QpI2cCvc6fiue8+l/6BsFse0JL8pvOZDYhy73kaVarhJhQgoZabwnyATtN5VltM8niedj"; - sout << "reSbVYi75bWEdYZrO1PESHmhr7lx+s4gBJQd7kz8wbRADk4dJBf8QDccgMO2yxf6biY6ezjB9GtZ"; - sout << "DnGrIuG867zq9fMbooU1PUrXEMYkeeccI3d3ClrF11AiOxFAh/tSSj05pwl7UWVKDXJrP/T9sl37"; - sout << "qW5Uy21+/Mi+kNMCaunB+fgJHsjvoQpwSmEb9k7b35H04v/qe1deEKPua/xXlawyTt4vv9OFwLFV"; - sout << "ovcegUF44BJCRF7zlZnLMzyveCE2Uizux/z5zXOW21bFEaODnvc9N8oTl7XkQAyDT67JrBC7WbH3"; - sout << "g8lzwSjSyC4paGxi87EifET4QFZ/wdhP/mP8lZXzkq8xieb2ad9dSCHS1T36XB40s2QmiSu/dXNN"; - sout << "yaGFiWtFCKYhie16fkeBuCuzcW98RXOUQYBVCbSaDVM7nNTXU5+2B5NfM6ts6+7fO+ovsJh74pGx"; - sout << "HrPXVdWPNb1Y0v7YhX7fLu77zpFbr5Mo1tdL3H1P8k/y+ep5/vsBE+git78nNPeoZajrbKuf3QtR"; - sout << "uKx/sDQrnt3opQr2k0BEt2HZOPeDw0HuPJnpUNg0u31w+xWZN5YsTO6HBpfg9x5hsYX9YybRFoWJ"; - sout << "bpfcNHrNzlc940Pbn7cy/XKiIsegvY8JKScIsmP0pnv/3rr3M+FDocBoe9QDnj7a044KpD2ghnwl"; - sout << "4uJjqFHv7RoGkY4365yAFnD/vm7tEvO09ibX74jzreb5lcSu/f0hy3IA2IbFEYiY1MBL8MPDBh6h"; - sout << "dKL6Jf+UHWrz7MM2bSfDzf467cr65Pt+N3bmX/BR5yd5sPyaiqQhEewGImg/bOiaqpRnIKIFNrF1"; - sout << "dHziLHvI5n/RvsyKPc1ScVFFHdGs9VMXR3ODzG4Q4wNyGYm8Bt9ciTw+Cd1VuAODX8fjGFhCRq5m"; - sout << "wlIDo6rwxR8ME68IXNMqztq1JPQU1VqEXWGAOBIewAYPLOepF4PR28SUgEGqn32oSG6hutM8UAK/"; - sout << "4LEsqVBnc1XrCczlpF2vad4Oux/Qd27JfspaVThUqSc2x0kY1Ca8Keggn3j0s5GCSXxzsQh0CuYq"; - sout << "jSJjKBVxbOP7XU9EHt6w4/9SOnEmd2QVJ26x5o/znpYpW1zTwrIG1ZhvU5AhBtzFtlLEOs1WzBH9"; - sout << "c2jFEPc221taxwBmLaOqa22VULgiW0zJ0nFkXZ4vfoJghqWOK9N9ux14hpKyWUtPPwPHT+oqctBf"; - sout << "RzNzQ/ODTSIkbFdJzskMR10TAYT3zsY27dWLgdAnmwXE1xr4SCbilLOEZcsJJDEX+XkRUsMbxKxp"; - sout << "Cr/IrdTuAzx5MogSWKsUj0W3cT+EG8Nv+iHw/q5PIag2y98b99gk73KWnswo+QXx6nTCwuSuON2s"; - sout << "KlVN2mvKk/t6dNJCbLNQoEnIxoHumQimOrsmgtD5rTXK2fR8rZlnBzUG5Bp/45YBEK2a4JMWivLu"; - sout << "nY+zNxxg0wslFV8OJaH3yfssBMr7lzi3jX0D1dCD86mSslQZ9hAx/pkgNPogz8KDbmoQjozc05Dr"; - sout << "nUKPCCTYq7OUxpGXu9JAFsQsbqnWIkqyxAg3+s4M2cc9+P+dFpb99cQAHDIn0LSBR57f6o8lojFg"; - sout << "nPtdvUF4gNJd1EVqy2ERrWmwRQ8gnzLHVI6wJYlJVUa7p6gk2yZBz4yRak1JJUgxp3l1goGG/0Ci"; - sout << "PsE78UDJQW+3FmtOwLuphs51+Clq65O/n5RIeLQpQ89mh1govqglh7o7nHyyulhQTrEpBEvy2GWh"; - sout << "LhFNp5FWc1ZV/yl4SwQEIbSpGk/MTmlPM2vZcOwTGNIxHia1PISF2LNDMDMmqQ0N8y/NlaKha2T/"; - sout << "ZfRhOQdR9nOgI0svhZQtke/ExHkpYNHsF4bbGisnGyzmtrKgr47s3l527s8lP8Pxe2g5cgX1focZ"; - sout << "aeI+CIeKPFynBjP5GmbCoBpOBpFfOgPrMU5cPMvBktXq27wF0eKbZ1pi3WvJo5Yt5SNlRbs8cX6V"; - sout << "dM0WDcqw39ff+2ztBdDR+aeFRmHelayiPJ6Rmd3dWgH5Zk/vIGEaSsc7/KfYuJQXkca+gR1SVE67"; - sout << "wez78yJbRDcoXiWhFOC4ilWR1DDKgTy7ej+qsbEtgEQcAqLukxeAnICNaIYzrQsPacqSvE6MLBiN"; - sout << "DcoQ5TnAjtFPGGkkhYH8TwD4kTMQgSDXt5yqADxbAMLu+XsmgOJzyAL597+yMJm0w/COAOvVYPRw"; - sout << "kgsDb3IXuTpW+bL/xGC9RvOefz1UoxpwP7znW1IYeltHBqT9JLiGLMQSRTl2x+OudBurZ66LiqQf"; - sout << "TnX+HXrN7MnTdshMCQ339YwnY6b1baYKnxVwNdIUcLnpyVqMCKZNu92bPJsdj4af5nZuIP8HMwoR"; - sout << "g29BOeXRb8UNtWd/EMGiuHiNwMMhpDBN/L4H1f6GS6ZeNobN9k613HrCeJmlXQDBWCHE66vVsAhX"; - sout << "X/Qwi0w5PCLwwpoIsgMDnZDLa9ZCyfBez3VP48ur6Qs0llb1j5y2gHz24yGH/fVlWPdnc0pbKvFR"; - sout << "KKW9jVKicLU9NGaOq5vLvBmFMnx2YLwhGMIlOgnnDLUkB7wCY3ADrQau1lPLfQ4QHr919kbUpCmq"; - sout << "3syJxq1j5248HvzVQRCQV9H35RIQGwd0fjwtK8Y60fxa6W0tD7B/hf1b9lrodaHQL4gYt9/lrCQU"; - sout << "LezUF2Y3HN9LxAWQNfoCbYaz9HebHTfCM+ynCUXAXabGngBLCat1GxbYaWlbotAXawMKxRplVYnB"; - sout << "mORrYoF3NaBlDPznNhgiU0Uz6t7Nh03r3wR8NMho/VNPv4XggWVXyyoNvqY1NDZVtHEPVaSlVq12"; - sout << "yUOiQ2qEpATWET8P+19RL1KAtCoRzPcuY2Ikir9MR8sTbog1FWyTqZN42P6Bx+darDn7eQTRmaHv"; - sout << "DrC97SY8szr6hZEA2ei3vM0JpNWKYTOHo7pTlaDWt8gEDt/oQFPUSk7WJmJJ3DeXCGOdnRABlCXI"; - sout << "NXDZx79pTuebQNZWIysOeisk2h2TXVvghE7J7QyqIYh80zqZn6nuAy05mtUT5igW4twr21r/X+W+"; - sout << "Eu+dnXFU/Cw04SFh4MVLMaZ8kCerQ21GQn0NSgFCPd/bfeJyonCdXaO4rVATPw8tgbsI93po//q3"; - sout << "Ps0KQWV//ZPdqERqZJYkZKZm7CNJRC9mcLsPAfnE8PRF191kr/A7BjLJu4UgxcGZlyQbi/uOJFd2"; - sout << "mErOsNiJIC7MuRTh3SF5baHXsjiVg+l9m9lBGXmaSHAHBsKpGqpFHF2UuJnkWihfK1QoNhEkUhBV"; - sout << "QOI63RPKJlgU7h7hi8fEzxzDbXbpWhsMwcKQ+j3UBSBzUGbRYznWar5vHxf8GQ1LklLRr+8jBJwZ"; - sout << "fIPGjtKXXyMNJACNv0iulszYy3XUM56cIdLXqjVgEu41sMUfJGvbi3aP0tX6wl2GO5gptrX40j6d"; - sout << "Qys2Evu2rH26dj/oCWFOqK0EKcH/Nfj/+EY+gm1GI6rSTgKq8RtD5J9fEe1nro4Sz+B9J230FWd9"; - sout << "G89GSwEnpPOE6hRzubd/NyFZqe9sxVIFdNqRlOsPbXC/gJw7Gx9B83I5j1dpYAsqWyBKhalrGRz4"; - sout << "UMLThFH6rJkGO0YWO2FF3Y59cWk3rS7taKomqL37407f3VWOtRH3YNhvxXI/6m0LV6czsmFHrSp1"; - sout << "lkpYyq/UlH4kL0c/m/P2w8JrXwD+1SwgMtMjuwIQsETng1TeEgdyRlkRnmKXVFDfSLQSppfq3U4Y"; - sout << "zrlnUiKPk511ZcXkg7TOkHxIMUpkhlSihUaBPwT02QpbcJR73i50BzQNDVQbQfMrXny8LEv2D331"; - sout << "mtjKrWCWThul7ReXcJV7p3LpWuJWD8h/6dwx8WuBg4/zKPXZst6WHD/xoEjYG8QNoDDOE+SylSks"; - sout << "AwuI4F5meYJNpFAoCj7qQXJKKyzyeTz2Y0nISbP34Ue1H01zu4SBN7OMvhP/Kl7RSFpc6JkV2brz"; - sout << "zksgJZ4vpuqdij2S4lQfJbtdrk8smc1VWsVtkRKZzRfbPmECuz/0aNHsWe7sFyqISk2Pe5kwuj3O"; - sout << "3mvWDzXBK05J63FYRnpemGdnvIkKLddEmxNecMuotq2s7fIWy2xeu1Ge6/8ILJmtwm9Bq3afgFfw"; - sout << "nRBQ7dj40h9kCgGUoAx0a2oEwTdvdgHp3eEjlyGMqdodEC5F7Cn+uSh89UsHUYbxAI/qTpYekaVh"; - sout << "5MRydNwdIg+3XCHQi+QGEr41DECSXUAoL+mCG6W87WE70G1BsvA3ofdGtKUxTXskdWVtyNip/qXC"; - sout << "f7Fez1i7Cd5U8rzkdCZD56P4KKx7H9N10aU5jIyfba6IKOG6WEqgf92SISTXCVuQSHqVBNHoGiG3"; - sout << "r1xxH8KEzApRHJF+whOvt+pP8eDVfVIdLrzDH7YP5nzQvnD1eVv6iZw0izQu7ArxMoqgSXOZlKBW"; - sout << "L3nkAohU98uR9oYPGck8HNiyKzuiI+DCeu7BOg7unI0WJsn0qE/F6EFEFYoqusBXRgDVwmySLyAo"; - sout << "+0eB/sUawsnUh/w0lRHBso36uynLjUSMBCwppQUU84pJ8fqr5aUfdGfWzogc/q0pFxppiexM6xHd"; - sout << "P8/lVcbgKEclaNJRkE401wDUWHDmtVAHiaZA/532FWyrucLOp6IRgM+eZAtao2/hQUtwOOcaAQNa"; - sout << "DcCN488KJmPQntnz9zn7Ep2ZZvsrx6fg9ubEEjHHIiRYSUDClLUqqFAJZaGrGsXmrSDxF0AEfQgJ"; - sout << "aZF7CJ0WBl4qyUBcIIeWr/+u7Xm9WDnD3Aowl3/RVxH4L9ffGDEMkCeY/rmNp2SDuWfti7vSrxge"; - sout << "NSQEaqiPPMHrgQ0vuGU7ipACRYtW8MgxSwxMc8AaAbhPQu8L/ZEC+/pfxDBy9mtMKnF8Jj2scJtB"; - sout << "GKhZ4s1s7F1fqA/m3Ts4rfA0HLY7bAWeXa2sNAdvkjoQHMLAiP/5BB9B4vNqVGLSrislMJ0sYfcr"; - sout << "t/O0wfihW++wYxK5HJWn/g7U7+lu4OZHco87HB62wHcvy24LqwNKY1w033SQJzkLb6Nkqj4HU9cw"; - sout << "E82PHpVUAXqR6FRqOSGX9bMMqWL/e66vl0G3hbhIwQs5toUgg3XVv13F1o2sTzjiQ+22wf3PEuD+"; - sout << "aYIYKQiE4R8kl3/96og7QmVfWoPxZiVEGBzM9bTDHx88W1D/CkwPkH389oV1Xky/L9UHd8adnI6l"; - sout << "kfuHNP/geDSReFNJ3vtqUo6RnWHAFaTj+x5GDJDtol2hC0KaDLJCETOQRXUOOW371wB3I6slEKjG"; - sout << "gANfqlMyzsO/7bxUw8X71uDns683wpW+Kwr73bJwe0OeP4dgwgTdTLQApMZncaphcpuZCEn9iSpw"; - sout << "GX3EO37EZR2RvLBMvlHhkZSAWr8TqOALJeFEuYiduuUmyM2xh7mLw73AqGA0Wm6rnc7oZwXhwaBa"; - sout << "VhwUZyv5rGMNQl/p5bMO49+tQiaIP5UXxheMCUN8eD6m7qDyHsljVQ+Madsj5iaJw5rkNcO4oquG"; - sout << "mkrQOI62kZGM7reBAZjYYjf33ttAZspzXJVFkNt8geb8e9/AFBdfB2nkJebf8aAAie13umwDR09c"; - sout << "44fUO0jtn43FxMBMXRt8SN3zVf4ycLW0WBYliqRrfhbOlN4wfzHNnII0JBO4wuDMC0zXnurwcLA3"; - sout << "SWng9i7SI/XH9YZLdEn1cEtkTNulFFsZJGbLci2EvHEnG6D/m041JrCBRP7oCGonxdi2zaKe02B2"; - sout << "LHaMeX7Of28XebQgQ3ZQrpSxW+h5FqIVNNo9EYxnqDDKyB7TeQnuISmSOwrcARqs7EgNAQ3Wbepu"; - sout << "Fciu2AFnzjsW37Di4Z+L8tVWDN4nZstII68QCzlx78pYFiWD7lBQvuMYXGPujFgNGXIW+uk8NM/H"; - sout << "ZWKV4UopvDx32VBudzMGJn3XPfrFqeMDukDYZXe+8G/jo8poz/kPfQmBqMSoHZyoKZ6yPNEeo1pD"; - sout << "JyR5/neJsSu8rCPX61ZMuInitxMKRTS8OD7RhcO5o0kNsFwXxe2AhJm2HbOCqSnGktciP5M3gvpL"; - sout << "d25K7cTMNvZIpmwH8eQmOoicl8IRKpj6qzxZt4m8yZU5yFiYzkMfEPjUwCmJs7tCZelrNnaLoFpD"; - sout << "LxraAAsxLtsTcIL9ajBequXTFW3SFL7jsG8WiYRSYmu60gWPgUcmpaK+dk6HFy8SFCsaxlTmCn4G"; - sout << "cbIFfJU1O45770/tHsDxNOBu/vsdp5yZw97L/xl6jSM9gTMdFbTdYw4Va3nr3L99zk9WnSYVZtwl"; - sout << "C5LtQR0xUAldgCgSUfM06Y85VismlOis/GxVfYqYSwN17u3Rv5xj8JDpTxV8N2CwRXxrq013NXoj"; - sout << "eDztUxX0NWUEDZuVvZUIBv9IO+9UaBHLZ834PTzKolkznv0dfedf47H18EWGxhXcJs/bqIP7qlm2"; - sout << "B3bpaGxX3BbRVzuGrWRccOO2K6K1wxDPBSh03z/qH2ddZsTPArbhqlvRiD7kmMDGJzX/Y2i0SuCw"; - sout << "nwhTENF7U/QfBFD8hWqi6/5rHArt/YSfPhaJyBOoRhTWgGc5RPlvMKviM7fE41Yhyd0vD4BqylQh"; - sout << "iOKpy350GOo7g72UBTjlUHq0uWTX9rMpghHkZrMt2bvNzUn9SCx31vC/H7dVjcuEjCeUH4xkVpWu"; - sout << "g820oyLQSkreplnhgE8IyG9OjJ07t1A2YcKHq0sr3t+TMLp23O6D2b/ZAYcnBFVIu1T2LIuX7NAp"; - sout << "tOmFBbO315b3PV8bQdywYBSlIcILFc9AGlttfB2ksqGF9Juhx7UOPczWeBgaTOhBFFcBoSELVoqJ"; - sout << "YP1KMe2EreYgzjVdUEAlbUh5R7mOs/pPF6XjoP++qnu9YE82r567jLBo3fmMGQJqURjgAIxr12l1"; - sout << "+O9rTfxhjumcmJo6iOeK6/wCPuiD2QosXIlYHi/nFlS3qJ8d5z+tU3R9mi6v6I70GRSB4TkFuHmy"; - sout << "5AX3LA05BcrjPH5LO/ulytgvkKsNIgOA4lRgZ9Z+whgI/MxvgGEzmTEyM2nxUFfzxlM4Abi210+2"; - sout << "WFu1QMDXI6MQ54KGwi17BQOckYEFa+0PSrxwf0ntQcgcGnpPNCSlSATUQZKCN6E67UTP1KRbak1K"; - sout << "nMvD3D9t061X0fAWW1txiUca/iO3KLYtMqAKgW6DZXuUzJdgtk2aqUYTKoI7sVkyVHo03aBBr3fT"; - sout << "optSJzBz8UIRxzRauuWo6o1mCyja2eM8fn7TMvSsZx4QkIF9XZA6UNdeUpbSzwa4XkdTqdzmoa7J"; - sout << "V3z+mkCQ/AaaEctE6Oi63jOlzfPmAOobY9Vp9i+ojaeo77SXu/7cwoWgeASY6zOHdN66e/zedP/L"; - sout << "SNYH+B7QfAvUgBLK7QuP26mtws0qeeGWPYCB3x5+pbeS/PHqIF578D3lasqUUXwa3Nz+OzyBGXBl"; - sout << "AB587DXXvbqK5gsoPE+mh4CJ0qYcUGWOhDejTpuEG50OQiGORtaswXqr86d+3vWdsmoJq8YFEWTR"; - sout << "Pd+TwEF9FO3qrN+R73YWEZ5nihD5eG2Q9ABWg+C9Us9KqtlPYQOeUjwrEBhAJZ1j3UT+OKHXuR6e"; - sout << "6dFh0fmlpXewt/gZ2z2WMZT3okC5JyKIMROWN7m+W+mBMmYg19gvib1KdBeBLO4sxdB/mElqdYYt"; - sout << "k+mwhcuKUWYmaSmnzXdU8/Lxyvfr+hpPIPJHYLXftm1DpNHQjLY2bX+UROQ67weTACWOtfz0VJGv"; - sout << "auzt3Kd5UG7m9nLXgk8ZibGOie2MWMY8C2reyrfU5sqOnLO9VNwazWvKb34cFEPKrGU1QqVELim9"; - sout << "kq/0gFdbsyiotlds/Va5AkeVAVC81yYhYRnQm2W23NY62dMMgorLZoxuJV3RPVUoAa5RKRPq346V"; - sout << "HnC7z8pqOsVfoslTCivd2CEzNQK/cYT/LJ7n2Kw3bhT5niBsVNyAjZaK4oI5fqzMoqC75ipQz1IP"; - sout << "JwLqRuBTBjipG1Boum2TahnMriKcAhV1vAiHcB1GkYf9Bwnne8KiWpVdN54drWw7otSWxpsD7oyi"; - sout << "nPfN7o9CrAcERG94IxkZJD3uY748ly7UrScLvIS0M/zcC3f0FuBHb3k1LKOa53B4Q/wo9FfffpYA"; - sout << "RoixLpFQRoFkC4l2wfMenZEgaYkYyfWoWEfGD9ucOiu8h5IrffBDzs8Ck8/y32oHvL/FlouHrmpz"; - sout << "5SKBIez1dkvdFTI/378ArjAwoJ0i2QsGyS/uO7FqgTAgd/9iIqeGonMnJFRPmH/VdT5mZwwj1BhY"; - sout << "2/L0tu2kF3uwGX4c7fSDN8AkPK+DexcvcRBwG6cdmFs1871Ltv0A+i2UTgdk6/Q7MGnhRxnDUp56"; - sout << "7cTxaOSfGTgKcJJ/US0PZjyWLN6tinlrYxkDq7uODDifVo8kEIOzYn/Zo7YbrC2FEXSqw7khbirb"; - sout << "5ErpT55vaggbD/HtahzrQeYIc6lF5oVtW5TuAAdBBwnPwtYrkAVYQ2KZeGh68JKCEbKfNoAeKr3Y"; - sout << "hhU51cHOBC8p6hm5T5p4JZppu15YmKxbLZIscvWCfriYVwPycUde10Nu+AixLGcaJQCr5LdWm06+"; - sout << "ZJPovwV5yErl40B+WEa4YTBm9HJqYXOI3JRAEkwvioR9TotiOOqCaoo8YtIHRU4y3YOUcAdqvtIW"; - sout << "Sj9eLPFLgTQ33kOtsZKDDzBJ/eVl6eSv559u23AuaeuI0ynarOjvQPw/543W/lCKibBGhuaKbYJy"; - sout << "igAy49KqZ2IBHGJEOxd/7e4pnBgW2Yg7T7oeruGrIlJk3BFKQxEOO9Usc7XYSiXMVXwzGDA4huZt"; - sout << "2zq/k193xAGK86lvndpmzpJ/eyVqxqGSN7yyr9o9Mq8mfVJ6CYGXgb+CybtXX1r5FfNTT7ikJreQ"; - sout << "koaSxOGLvHfys0h09POLbTZ4jXBVrcuLuYoN2Dg6vpnMQSEoZzY85NOGxyCPUHXf9KFRfUe+r5zp"; - sout << "O4NnC66UUzrBan3eTYs5X7DEksg+bPx4KGMQ/byJm4Jr8Jzeug6S1qV1diRpkgR60KCS8Q2zCmqa"; - sout << "Y42riGdaBXcaZ3MM1onSKNWiIWa62QGu/GuwY64JUXkSTZViCgkPbieNk4J9WSjwKWPHwPq97qBy"; - sout << "UcJb7MNuA5cxGXOUpUgo1I6ewBWZJvTIU4o/b+hBbBntfgacsZDooFmU2xUYycL0/ornfsEuJczW"; - sout << "1dI+thnc10hcf/WWhUpOuMLQK3f34AUL7tUWtsuYFaRN+1dabnldREIXMFnZs6G+gmbaX0c4TDLh"; - sout << "i31s8ch2irdU8aSTT/1J7EkMvyDZt7NLHeTRgRRrVtGg8nYbBrB/MxwqGTjtD8X7+MYzloLLiUrc"; - sout << "7ST+dShb/DY4JS5RA8I0RBJbqOcMYzdv61UaxfYOnvZHtbCGlj5MmIMaPCDe4ZTSPsk1+NhUZSKr"; - sout << "NcbSth2EUMRZsJqDLN5Xh8LFoerrFLNfbb6r/Zo1I4T7NcVaSKim7u03ti6xCQj4Ds4yqDLrql0P"; - sout << "8IP3w1WmY07b+YRN9eqS7gFN4ReqCPVlk1qnZOzzPjH8pUPqgb+0cjuvQyJslbT98FrPljGtXbJ5"; - sout << "si6Cp+zT+aIEy9HTtQ8Wq3ojrVLtjMl/aRNPb94A0ncTZYuoZCnOC75i2lLru7HOwHCdA7Acyt4M"; - sout << "a7xgqqPiV13DaPpOQYp5rwKHNelo56FyhrmhBC2qQznvwqLJPRcnMEvfsg3Prs26ncYOOwXJ8DK0"; - sout << "GzXkJVznLs71ggUJY00mOvTS5+5sPWwxyjKhdckQfSil9orOJJWeDExnCVZYuIWBij5VXQiACLyN"; - sout << "WBKo52tef5NJwqNbjInxKmFChkX8N6en0uHyEAPxNLrYrn5I8+HibsvccOOIwb9yeT3TV/DB4SiF"; - sout << "rKq/nLdDdQjbReZfZUHhjKz7i/QB7eHinoBVgLcmAkN5lDJVec1Oq9GNwqKTpM9hzHl9KZueqC0h"; - sout << "EJILZgoJDs6Vy9NgjK+FYD6O/2Uv9CGfnCL47ziWfyWgLX2UPFX8DTrnWG71HfNjdlWO0oAmwJxh"; - sout << "0zoURT4B03KBdm54pk6TNeoThxauGpNkK+vU6qcILlI64ZFxaO42b5rjQXtnmYEGL9d5MroM39vC"; - sout << "hykPHjc2j2qFIRUQ2Eg4mtSR++AAriXGpDu3WuIygTiKMNy4Jbt8AnInqnHe5IeWgzE2eAomR8Yp"; - sout << "ZkI4LCBOIPRQbFZ+DnX9Ntxqx/6rJmXE9njSqgbepHd4e38SMP9iMBR6u5fX9joI6rcRikf9W5nu"; - sout << "G/TiDJNhEwvSOtgdEu3pv/urCx7CFwbkhSVz3r/HbQwup/g5FDuTxteiAXYn9TKCWXomd4oKtip5"; - sout << "aZw1aIIYHKR4zNPsNPw6uJUpwKU6aaJjAjXJtaB3Aq/fcvuHdCYL0AkL5AAItGnCq3TOli3VAHg0"; - sout << "zLm6OpVaCUravHHyYSuwXHB58w/zjz88c3tlOLRJO2CoFIUzvesT7FkKN8+y9itHmIdvgZCCWbm8"; - sout << "uoYwp4U7dy6bBVvcxjca4A9f5BcsnZ0u3h8JRxdhMpwk3H1kt6PwEFTrk2Gb4IemrBh4GugYC1NU"; - sout << "ZGgtiPD9Lk9pXqPb9LHcY4nWCEioiMUBd9IJlcWa5NOnZe+xeEHgVpy0vYrtqVHsoRAiXzRTxgaa"; - sout << "2RlSne0P1g0NgU4YCnRf12ZIS+LvPMbYe3mXfDpn099AvuQiUngIFGKm//F59pxxMjZg/ADElluv"; - sout << "0eOBWtYiaYVDL7D0hiffIcBcKQotLJ5p4q7t5v+7oowQ7cMyM4hvL5IjS3BqCpKNwWg3PA9jnhun"; - sout << "sAVHJa9lLMk3cdXix+BKNhfq3IfqpzXAMdlk7GeKbEl3F1/+q4sxVq7sd9X9XLNR2lhJVy93soM8"; - sout << "HfWHR7qaHnA/7XlDMDORDxuT7H3IKxDa+TkORiI9ldqwCEzC0ZGDBU3rBBxNK1jzXH7vzQ1vX7x7"; - sout << "eqYl74NRYH8j1jDxWeQx6bQWSy0V6N+c8uC9N9HPVuH/18KArH+sEDxGRW+Hxi1Wkls6+QyBiweA"; - sout << "tlHueW6h53s3Hc79AEK7gM+40nwvjCOG9vojSoI7bc2ndc+gdqRxYdUz8mu79bx8CI9Y/WlGO/Vk"; - sout << "opsB2ETODt3OdS5xMTUZfSO732Hj3nQUTmVVheFbxDpVPUXNx6AAp63iRR1oYv8b4w6rXrjpj2ru"; - sout << "8U+EXxv2W29LDJFC/JRBwj8JyTR9lDhARPqehW6EZmUYERihI4NZbM9mhgszbC+XDiM5rL80FVz+"; - sout << "4eIA3mJrPNgmevZC2Y8osV71pnmt54foH2yERar9tqSS+q0q7PWX3+5jIrM/IDT/TBrHQ+Boo0y3"; - sout << "bz8JHPnwCKXv34Kjg4NxbDjFT0G8hL1+LsNle+d+PyYPAKSLOjtzDicknXz3M6DgjwMrzD3zLf0O"; - sout << "jUlwIR13B4f1mwH/cOatF5LfXQRCmUxJt/16/VtPsB9FuC0sziuda+4/CGAIjJ5GB0gqzwN7DSOt"; - sout << "7cNdIO7w0CTNOOfVq9C1zPi4fndOnX9F3M+sPSzv7YjeUbkchXzJhip96etsRY855grSR//reW8P"; - sout << "XNodO/FvibrfkHKpG7fZOU+CRLqnikcNgIGBDYumpgdWA1ynvAOFPgAk5tqtLZKmrIe3ceptlG6X"; - sout << "E3UDGPMNVgExa9GFR1e5bIq0GZCNeIyGMq8cGLny0uRC/u9nPu0NoTMxFb9rr2iEp1DJB/zyiped"; - sout << "EoTpBPDptrXVQ9DOLvpZnplsmuuCMELZ7eP10CaDzNXodNNSFr6bk5oDTHJG3foIB/VHzdH7WDDG"; - sout << "DKMcvwwXAB+gAe0Uz3C+W3DlvI2xAHxpjx4hPvATyJwVgYLus9pHzKzZCJ/gLYMgcgyArdTPI7JT"; - sout << "ExgJcLp8QTosu4zuYCJ6GgPmtYlVtUdLWiPz6lHCW77bkkFmz+c6u9WF3yzzY1nyhecgnt/7jbyA"; - sout << "iSeDme7x/u9K3n3CTHc7+ZXHLT6HSdxKlwZ6811c5Z12X8aP6vvL/ysGTVwa8lRngJOOiGQuX6GT"; - sout << "PMohKZIhYGudZundQNXPOCavN+dpdT5m3AVMN9IrU/ht8o9eH+Hmhs0gw/iEwos7GgEJDl7IUcAg"; - sout << "TZb8blBMrDfdTyAKjYcuj6iA8aDYes9sB/W8R6AtNRG4S9vQcJuCD9JBKjxwd/5jG4hrpibV5bMD"; - sout << "F7dW4/5kQsc3AEdYIeGITaDjXorQ8UZ4zF9Zy81kTw6RAoTKdMYtDgnEffkDSRWGUqySn1CkTs/p"; - sout << "cdY3SRk1HMd98JwlVLpED+mWbcLwRuROBimfERC048b/vfUXzGSCgdYZ9AGQhHPzu8y/nr7Lp74h"; - sout << "I0ghlojt4zEkgBgK1JRMXD/dxwqIu+PIOo6s+8vwOMCLytvqCcTR8hqs63JgVGJ0c4/O8R1nbwFg"; - sout << "iQiCrQ2EjBbGaBMq8vhYNA1r9QzZwHejoHm2Z4a+JXoef2al/GqOWnuOUHBotsp70MwvCS8Q/EtU"; - sout << "TFGRttRKcrm7OhNmeiFLAr4P6C6O7gJxMBu78s0bIzpSIDTZFCZRYU7NWdSysmRq1ngerD9inrKg"; - sout << "zt/qQLnry4PmY8NjPwt7QYcQziZLN2ruCtbvk2DrPVSx24HDHGvB+0g8jLNLQDfS552/Xs2G4zc1"; - sout << "wwPpayeQy7HFGT7Gtf3oCI4jb35g8MTCXoOMgM1Dk+EYJIyDT2PBae1AcDcDVj2JKOI1UbvanFI2"; - sout << "sFpsLkIgj8SVUI5dX2PEEl7AjmoFJqyeVpqmBgNEfNwPEqfNNLUp6mic3WusrWO/jQwFtUIdEWAc"; - sout << "tjyIqyXeF1e1zO5po9/kJOrRJFAFxMFI8PVZNSQJ6KIQDhjPJHi/z5cCaf+lxqSo6OUt0D7z9o5A"; - sout << "hfA5GwHX6oy8tGqommBrb6nF1AWVInk19mynB6E17TQRo3T5W2OFG+BaMRO1xlg1znAurKht21fr"; - sout << "6VZscHHtctXifsg4dequ2SkchmjJ5yWpxqRqckES9B3nVTSiqn71/T2O/poDieJhqADWNw3gj96c"; - sout << "GI4vlUR4Zo1drdvtgwHFFR+RKndNuzv2BIYvB4nm5SBqx0nuKcvbbxkEEhIRVOb/GiMJ8ZwjeVxy"; - sout << "b07lDWMdBxLyxytbDoNZbefVc8N0YFrsFvrc16W8y4XKS2NG7Kfa4u977Rr6Yd6+u2+DIYwfsrzM"; - sout << "DWs67DC1+vHawIhIQ5NyB/R6hx/Tym42s7IK7l9w80SaBvpN2A6vaDlNlOD7ANeZsmaAG8L6zFsb"; - sout << "FLBPRNFxm+HxHYjpG3+dBySqcIyK0tSFfykLVffPMFCtgzTwDM3+5VJEghCyVlGEhDl5tv97jwVI"; - sout << "aTYM84hH/JqKcDUQwmilCXqbb8bh/kTEWKzsuuyaLxr3D6+5MhVMakjG3RkIKfjddRukZC341efa"; - sout << "fFJVWanSIf3lSrhFjsiH5k63EjbZmAmuMpfiLZ/Eh+zjBbh0dE1spYJ62S+PwkIXMTjCxmoJW46O"; - sout << "5Bdl71SqZW7lnQUubNcgRlxAZGKY3aQlIV2qHptzubV04SMqdcyVYPrPmvZSM5U40eDoJB9UYB/D"; - sout << "JaxlWFMYrW1ntJTv+LrtYepKdD3uC9SLKi5VYrWjx9WzRMxxkDcNt85Ak2cfpA3MQXI+uT7lQVoG"; - sout << "vDM472l4Ruls317G1X95HA3lcJNseSQnFsAeE9GhE6XXmxDwIDqUQ6Mk+F71rvDufNIj+GaUTISI"; - sout << "+lwC4tqse9dg+7wcqSTqe1cx0K7tZo3ZwR+Y3JEe+6HyURImluo6BRV0n6URS2MLpgN4geV0m+6z"; - sout << "ZIML0oUAKyJVdyj4XWEq5Q/pPDvz7jDfKyqcHTCwBCuBQEnhsxi7Pr3yEac/dwYDKRSkX+7Q9mdo"; - sout << "lAjgzkEnxlR2JUJxtrsEuhTiWql6HjmrkT6ohOeR/k24Y5cEiKx3QM7gBcB57IoxxMVaDJut+Q3x"; - sout << "o4HQ7J/IRtkKCPp0LLSK4Ue+nSgJY5LWtD2a4KzmVnahmXeYXCuAsKm3+7dGFISapet16Grci2+j"; - sout << "ETdZGgs0DEdZWncLxrCnhnvXHrfsSsVyqocOtTs1KiwUgGgcEhlSBZyH6bpF7IYhQhPzML/SIwy9"; - sout << "ByDnFZIvESXwQObXa1iYfR37SyV0Yw/75bcWAnx/+jbodjLZF+RMskqs/i+PnEasmlC5wrjYo55o"; - sout << "E0tqAakxpTNP8+TrRgNMLFP4tP25lfDLUka+pUXoeLC7gWQ0MTA8LDx5GyMlblaIFGHx/eMZv05r"; - sout << "he2nLJgKs5ZtTMh+8pMNkEBWXQDKyJn0oVLcRQL80bPfpUfsnTW+jrEkmH8azM7CN028QyPEhBPx"; - sout << "lvDsEjVbElUqBH+eo3o0CYLpnABUQ28KvsJQhHAsk2WVScBv/hm/T4c2zABqaDOl2LkRCK9ZeF2W"; - sout << "kqLNjVH/iZBVQcjo49++lINm4Nupi0GPnWpsuMnNoOdZzK7Oygqbxs9Zz5VqdjRCpxQGQgOhAD2x"; - sout << "aifYz6RSk7i0RB33QkjE3u+MMEQLL51zl2EOREXed6AJT0U9D0+hSLH39wqMbdEV9bIFcJ7XprJP"; - sout << "ueO/zZOmCHIBfz+47Kec5BAWkufEOfKxOxwV/Wvh9+VA2RcbA0exvVj3Fy/hPmRaw6E+SBZTJoi0"; - sout << "aQltfhFmfLQtT4SRl2FjDwlpNJHjmbmVOALldNBRhxSz4oWfurPqAB1A4MazDorJI74vqElH1SMQ"; - sout << "7RZI0hDhS100VqCdgnYhtMa/We1kNuLcLi7cSq28JPUE0tkIE8SVLldq2Ekn90XBP3mY8LJe0WMR"; - sout << "J/LzfvopGKYrqday/eYuiXl+lihXp0dpph9ZUpRzmAYWtns/djnzFSGS3bF7VOOaFuGxwYHi/1p1"; - sout << "SaiBJV2a4U1vr0mh2OU3wMuqd/t5nfl+Sv75zG3YOAvcoiUH76xxHvRnn+D97TegC72k4mUgBCPs"; - sout << "NZJUhjyKF5S3rerk5uhxFmlAmy+XMo9VJJaYs6OxiIeSQa3iAnbnWDzY3m1chupbDYK2fs8tBg3c"; - sout << "ta7LM5SgWZLMN5Lj1RtEbFBpAylXWrb3JXGuaK/C2biFNTSFU10Xoit4NcqZuYTXu6OhNahZqayp"; - sout << "IYXTMXUy1KPyHHMFWZ9gXXB/igOg80NZ2O+EYHHfRiqSOFJ7xhnY+H4X0kh9v6N7lxlv1WOudRnJ"; - sout << "lWg4FktfX+JVDflDvJ0IqKuM77O5fgtBpRL3tpS2dn4U227ZiXRybiv9zCnbpK7cfq7/t69AVgUG"; - sout << "qjyTGZ+YG+PDjVBWC0KGsJWIBIHwCoTgIZO5kgU/831jcG+LyuHWf1Q9bIEkOXy6UzexKYCJgIaf"; - sout << "EH8IsTAVe+uUvVSBpr/Eis7LJkyx6KOAbDR3eXjXjhXJTSTK2efctB2Bb3GraweN/LUZ7suO16Ng"; - sout << "odHLPnS7jsgag4B7sLNCE3NeGfd4VIE6bYg/9+Ci8k26WMJzOHIO3H+Kc7cTPCdohAQS93epRcLz"; - sout << "bz957op4v9NBlgMcIKW3Fp1opHX/+vApvBecnNDF2oJ0bUq4pFuM8Ag+2PdSDlJfcdnq6S0lNA8w"; - sout << "H/65WlKeVTHRt0nm/p3jJ8JNUlIZa8iZyGa/vpCaLqymASIcIfYEdfi4ugCOvbKgBOT//4cl8kji"; - sout << "3BpcImX1IRx1UzgcIcK4lqO8WoSpGqhClHHi+/ezdj06JLKP27zKeHZwhTMAyYifDnCOJG2XtuY6"; - sout << "gg4LTyI7BcZ5jAogPeibS+GdwlEfN+hOk1idevzrgEdzJTIR3i6p7ugPS4unLC2+waQfdJMs4Vjd"; - sout << "aCmDdalKnQlpHL0Smr7Ezu0P1jPQcyO2hGupDOHl/TslPpoPcA1N6OSTraByTWL4yo9aKG1QDIOX"; - sout << "NLGvos5UGUwmPxJMTeBGkxT7hyKmNzMmhfKB/yfqDaBaV6fosCm7S1FVsdnU4PvcFxa61Jrtok6n"; - sout << "/lwuY3BM5VCG9/kZQtVUtImeRB4hYy8fETZI/WH+WSGhZZaum+Z7r4Ngg8/DC21U33wyR/WElnVE"; - sout << "+Ny+yHoK9xkiLpLlx4ns79XnKV+S511udI6XniRyg36kCNdRWTfN81HfbS6h/KXzl2ByPVo0Q9Op"; - sout << "lTF2Ox2WJ0x0RMrE1SyS1KMyL1ff+ToYLKarrgalvFUvSMI7cxPlc6phczmtYojlm9R7JdJDpuGB"; - sout << "k9qA/UEnH0v+wl1+5rt+RZ4dHtD4FOXvnqW3GdN7XvISIFJLhq+UMhBPiI4M39ZRxGpuROiF3Dzd"; - sout << "gD19YX9c930yj3JEhkbYzjzwa9CWLcnXbSYe9sayBJmCjpUxYmfrWo+mH66POo9ZxJeeEOPiVS77"; - sout << "HXv52am6J9m3gm2Buruibflo5CGo6Ngw9slSm2rcP+pGEAyKjQoLrU6fb0TxqUnRB8q4PVvbBRFj"; - sout << "c1y5MU03KShFhFfcM7iND5az9RcBJwyNZtCZr7OxlUDVDJP5IJubY/5dahCfiX3EfMXT/dR6R8Yq"; - sout << "jnUNqjVGnIVm456KF1xlEZBDwCL5LNUhGCV+kp90STd77lypcQPP1q8iFDo1HhHnJDj8is+5eMY6"; - sout << "5bfMP1EP13tCqPnHgD4hK7Ega8bkvoEKZInxohWoaS6F6o3lslr267EKKvjHDmnA9uGWzxss7nuj"; - sout << "3PlaWuYpU9NfJp6Eq0xYNYcf4qLTN3Nhd/GRpptxzdMCuu8x2CYKBkHKMy5iJELoJ8ntEdLtVdd+"; - sout << "46L46j8oPIUyswAbmodfp82VIodnJ8xvJUyJWRCXpCa2esfid02xvel9h8w1V8Uc7BKu9HMsFCDn"; - sout << "+FbebdVIahMe67eUI3i9+mSZ3BNq0qbkn/dDabGwixw9lsCU5uyHZrzafG6lIA/MiepNzIRLO2Wp"; - sout << "iwhMc4f4l8ZRb89HhL4bjl0Wo44VEBP+Sqf+jWMPS8qo5st+MZbtu89RObDH4fOr+1v5dvGkl5/d"; - sout << "YROeKTCWyAWWSSoinCHoR/IcBi8J31l34Pk3nNrZ7BAyMn28ZY7mue6AF6hpieJ+o/0RRv5m6VM1"; - sout << "1OpAFyajq7E9/BjrAfRSpCtoicoIx+APfjrLbCkEehMCBZoOM9USTMi92Id+Fm2iI+/AMStEU6tJ"; - sout << "zumgP60vNm3VwZw6ep1m1DSb+2lduAxtuDtaspztXYx3QhksLmbzeKD91/v2Mu/JveEfy35tf1kY"; - sout << "fcFtGY/puCXnAIxEeKY26aiZroOWt6HLIafNvD4wg//A/9iTszmQauiTWQbBLxcK/KreAE2TOpJ4"; - sout << "klwh8NfDhULKVKoJ/AgNS4I/Y/LFZSX1VerAImW/WQmpkqvjnOUrpLNkuV2NjA2tKP1G45dInmG/"; - sout << "nAnaPoGs/bAdQLIJayWx42xsKM1pil6q86WvoKcEH1WrRRXsRJgw0dU+Z1QIFlC+xKqP7xS0LZEs"; - sout << "fjyeED07SGKBhUj1kbSp9E3PMr32yDHj23zmnFyBcwihktaRV8fEquoE0CacrSoxfdxzdWIDy9Ya"; - sout << "6tVBbCcxWoz4CSGzBKSrEXVjedHR6H3etlrGwzPWYwbiFQQu3oSNml6ORLh2fu87qIjz8VrO2804"; - sout << "dAqaUcJBtHOOB5KnNMSyZWA1+62WX3P/yJN8Be3AcHY7xf9bP1HUxyq5cWcAbtx9yJV1dhXyvPz6"; - sout << "D4k1sntDiWgivLkY7l3OBVRs/QKGOOTj1uLei/v6X1vR54lH5wsRwq6niPYsyEcCnWk2zzTKOcK/"; - sout << "OBhLQHtj5Wxf+NQ/S1zZnIBS0cww5BqZD0aATVsHfifZLEw2eRM+vap33o0ozqXIdbCzzsotLwlx"; - sout << "CfFT2a/MUDatTSTuGjOPpjbmVtK7qWkWmHAF0j/GQFs73K/8PGmAWomFb2WWB8frlHkkJoEPyHVR"; - sout << "N6hlsAEd8ylb6SwVawZglxdpqfhQpn71NaRU/ZJnk7fnSVmKwcGVeh1sppPYKEu/Q7i4YrAGtLDp"; - sout << "Osipwqh5gL/skuxQwsBI/VNqoxIELFbJKhGrVqr2kHWB3yE9cB5puaP/MPPvIwbPSNRZVVbvGV+m"; - sout << "Mmi4LDSKPlxDmjl3wWnzdldnNUb281O5dA7lMuPHgibIFjl8EeVHBFLocpnFbSLRiCZx+xh8sNRX"; - sout << "ti+5Zl/pGvlloP2k7yVNRFpB3Av30jzQePY/DOy5DoF2k4eY0qvSR7HtV2IG+9wpBAUCbIaD/2t3"; - sout << "FBcQT2cWEx0TPZbBqPLAxruNGcv1z23BdWUeQpeEV6ij78xVILQegtNiJlCgMTCmIRr1meCfuVJ5"; - sout << "vbvNLxRiL4JSZbjKgfonK98os72YhGB0Y2oL7ZpcthGFKCGAjo76XrMB/hoeXlx6UKYlLknZImWO"; - sout << "NQj5SAhZElv+LKdSZ1oc9U+0Qm8K4G9+8OnQ4wS61R0MpZKXnfFAMp99XHqN0tD6W5Br22/IOyj9"; - sout << "x2QH50KAfIYZFrJaLt0MMylpVhHPQ1RBembk0wDcwDt2phnU5nm3m22r81uUHp5Sc0Or/vrzpMEc"; - sout << "9vqhEFhFG74o+EGTigUiyzCGUHJu9ITqb55xDmPm829KOqkOEV3L4XX/jLwRFVkbAHaijjmg17xN"; - sout << "yJZkFlYG/RntbFd+p+10JuhI9ahBvvCjFnkC+8/F64Umx4jlWJRPIADPr3yH6tt+nVkC2Cbx+U4y"; - sout << "bgINetMWF1b3syNyJh4qJy/KXX5UckzU9A1Xkz2MvXWiB8Jp0f+Q/bZ0inTmecpyBGWCsD0xRYPv"; - sout << "Ktm2zqD2HL3Qtfpyi0khXGmUDKwvuFnjX5V57ustxKCq2h/ylqTDCMpqS/QQ122Tr2DuHHVjGwGG"; - sout << "PajaD64n0qEj+zaqyUgpDZatEVMgFxpLpbJ+sTyBaYKYPYqGxoo+XycDYIWoc7K+7d2NT76qQcI/"; - sout << "LHo0kGXRTNiciphMn4EX3PkH7zUqR/yCWBRsP4wtPax6aM2m6IpY3bDbOfwMzXDTZDLAfi3is8Lb"; - sout << "H5AfK2DDG45w4olFJuLKO27zafuz9GIAgoTsUwpJIJeR4xdtlP9EtMjG6SZdp22+MBSh6w4CXe82"; - sout << "iIquldB5HzaHvBr3UP5LGQR3W/Wky4udFj2+XCpKsDUll+4MZCeGz1VM5ZYjDKfRDzSaLjJspoVH"; - sout << "TT05yuagEN3+w/ROxo419ZEnvR+X4QLyp0JofhFpSsr+J9fQwaz3IGpPMs7wA+Q9CfEw3FkrFfeH"; - sout << "022qfnq3naUsXyh8xAeI4r+MP6WGlscgFkWPZhM/vDq2IcIbCwZ10Ivx8r9bwi6bAfPE5cru0oRf"; - sout << "YYs7kU0l9z3yzeSvjVjkdZbmgRNPpED67KlUQyRibkWe34KGglSuSClFu6PWrfFxb/9ch0KTFVQS"; - sout << "fo3NDryMrF6oeLHe7osiyJ1ismBimKN3idmZ7yUknYbpR7/Z2K0LbAIttidcQ8LpgUzy/6jROtHE"; - sout << "MRLxKl4ba6tx6BP8WpXWVzx6xYox42qMX24SkhhNVR/9ETntqyFEeDXXlS88Nm14AZARVAp8wJ3n"; - sout << "WaA9OydaCeVzFVZ614BRC41g8VoHz9iZxHF/7fL5gz+YTJH7ccf18hxv4U/y0ROiZAQflTW6+lYI"; - sout << "kIO8t9A9eQ7Djh7cOHCyA7E391UriFUZH8kBzKjnMeF27uYx5U9acC5YUVtDk7O7Xq3wqeGaxS1b"; - sout << "oTVKitvXCbdmvIiCK8N8RZeVlteuYhJ20u7LTHsO87c1Tv83pu9OiYDMwEpAFLwfHwi5YfZecAIA"; - sout << "bkkveDusgnh80pxc/24zp7W42Hm6hfq852ZLLWD2bHm3YZdey2WIbGsjvQS+io4xoEKGLHUOi7Vs"; - sout << "/Te80Yujo61Y3BjY8w58xNplBAXvK4Fn2EenYcDz839o62+xeUuGN4wSUpJF94aU9//hEF93J9Zf"; - sout << "CxFPlooKe1W7nU3sl2ziEh0PeM4RImmQ9zVNkDEx8ahdmvWWSWlmlv1K754XAdwp68cdfKUIY8kk"; - sout << "M666TRZrSCDUvDufPL4y1LTpQUmIhtNLkrYZg0WEXx5QRF00x90VrsIFcXA/3Sjc2MPi0iMck6vM"; - sout << "kh4kYbIqlvgQSwTqc6XmXEM/W3HRuVCH8kQKIXjN5my0C3RbbYIr4vAPaQL8PCWaIjbLSHDcutk1"; - sout << "LBjyPnJQzn2/vvm+3oKN2JXPxeKvTbaoYu7EvWtXxkFJlrKY+o+FQfxmtN8qxniGWVLuLX6k8keB"; - sout << "wCLZV6wF1XG5FsqBoVh8qvAV/Tykk0VO5FJnoJ0sVwOveUgbCFKsReM24lZlgzHsHFaTN4qSWHyi"; - sout << "BGg3nr0dETiOyEfLP/ckF4GhGMMQZmT9jMFyUEgDfszoDgF+jxA1buvCKTRm4iVgFT16pi4wTG8I"; - sout << "fCOM+SU2UltyA9EHdOW3rm8P8w4z0D0/IzH9Vd/O8Y2kB4TUOW4yHdB95MBCJbvEGYXR/A1eTsMW"; - sout << "J96ZVGCmlcOzWqPsX61LShr57TLbE7+hJzxUMEW+ri+goqnSehcpGurw0+d4K3+5jFHxUpNaT5n+"; - sout << "t0p0S42lOvrgigSZrQiJozBCCpm4sCCM91bzGge0VpyuIGTVkzbzXfjzF5J/wliLSVyzqSNq+HqL"; - sout << "06BqMB0leDtLT/msAUV6SRKr7/n6qMgkrIfrAri4d13kZTpexQTRTFXxcoIgwUelzsxhdwZQ4yMX"; - sout << "dAeL/oKOQdjCgnsVFD5BD7clhTEOA8gVVxASHinziac7ZkdYaHwESDZBxJhv4j1nkKNLWZLifph7"; - sout << "NdJBK51yqlBvkZKROBbTiQi7UCe/YGXusDPCBsu55iieo/o65HkF7olpANKxvHxBrQuCTxYnL1tl"; - sout << "pMjxmZc1BA2T7vyd4AzXRJ6tMUdqUqnjPVFY7OIKMgUn7qRieMqt95vzJqh8+jdApnY+xwnIKosv"; - sout << "ox55mijPLs9oUBzAJPpD3nDLR9pnTVIkY2RmVRQFUN/kuJHYbNtc0PRIAv6iDiZhe+jeCkTx/dXC"; - sout << "sSVwD5hp/v0TvaPa0XSPr1BbqlvK6KjtdsVJsUOjHFskNm/8qlIGKp9F5QCtLOBhp1eoy2AZlNlN"; - sout << "+eYQRzwMSsJNxq44rixF97d7qeiOkC/Uu3wNk7aL11AR5iS7gau10LHLs3YhMbUcb+4kf2j9NpWG"; - sout << "wqMklOYYJag/XNyoQs8g44qAha1rVyeq4eXodi0JegvjkXWEB4Mq8jBuHXbYjYiRiHoL68/9mry5"; - sout << "nlN2Duwp7g5yl982CZLZc0k7uSjKaDkWyynH60MwLnmVj2sA"; - - // Put the data into the istream sin - sin.str(sout.str()); - sout.str(""); - - // Decode the base64 text into its compressed binary form - base64_coder.decode(sin,sout); - sin.clear(); - sin.str(sout.str()); - sout.str(""); - - // Decompress the data into its original form - compressor.decompress(sin,sout); - - // Return the decoded and decompressed data - return sout.str(); - } - -} - -#endif // DLIB_FRONTAL_FACE_DETECTOr_Hh_ - diff --git a/ml/dlib/dlib/image_processing/frontal_face_detector_abstract.h b/ml/dlib/dlib/image_processing/frontal_face_detector_abstract.h deleted file mode 100644 index 20815cd0e..000000000 --- a/ml/dlib/dlib/image_processing/frontal_face_detector_abstract.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_FRONTAL_FACE_DETECTOr_ABSTRACT_Hh_ -#ifdef DLIB_FRONTAL_FACE_DETECTOr_ABSTRACT_Hh_ - -#include "object_detector_abstract.h" -#include "scan_fhog_pyramid_abstract.h" -#include "../image_transforms/image_pyramid_abstract.h" - -namespace dlib -{ - typedef object_detector<scan_fhog_pyramid<pyramid_down<6> > > frontal_face_detector; - - frontal_face_detector get_frontal_face_detector( - ); - /*! - ensures - - returns an object_detector that is configured to find human faces that are - looking more or less towards the camera. - !*/ - -} - -#endif // DLIB_FRONTAL_FACE_DETECTOr_ABSTRACT_Hh_ - diff --git a/ml/dlib/dlib/image_processing/full_object_detection.h b/ml/dlib/dlib/image_processing/full_object_detection.h deleted file mode 100644 index 1dfc99b2d..000000000 --- a/ml/dlib/dlib/image_processing/full_object_detection.h +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (C) 2012 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_FULL_OBJECT_DeTECTION_Hh_ -#define DLIB_FULL_OBJECT_DeTECTION_Hh_ - -#include "../geometry.h" -#include "full_object_detection_abstract.h" -#include <vector> -#include "../serialize.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - const static point OBJECT_PART_NOT_PRESENT(0x7FFFFFFF, - 0x7FFFFFFF); - -// ---------------------------------------------------------------------------------------- - - class full_object_detection - { - public: - full_object_detection( - const rectangle& rect_, - const std::vector<point>& parts_ - ) : rect(rect_), parts(parts_) {} - - full_object_detection(){} - - explicit full_object_detection( - const rectangle& rect_ - ) : rect(rect_) {} - - const rectangle& get_rect() const { return rect; } - rectangle& get_rect() { return rect; } - unsigned long num_parts() const { return parts.size(); } - - const point& part( - unsigned long idx - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(idx < num_parts(), - "\t point full_object_detection::part()" - << "\n\t Invalid inputs were given to this function " - << "\n\t idx: " << idx - << "\n\t num_parts(): " << num_parts() - << "\n\t this: " << this - ); - return parts[idx]; - } - - point& part( - unsigned long idx - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(idx < num_parts(), - "\t point full_object_detection::part()" - << "\n\t Invalid inputs were given to this function " - << "\n\t idx: " << idx - << "\n\t num_parts(): " << num_parts() - << "\n\t this: " << this - ); - return parts[idx]; - } - - friend void serialize ( - const full_object_detection& item, - std::ostream& out - ) - { - int version = 1; - serialize(version, out); - serialize(item.rect, out); - serialize(item.parts, out); - } - - friend void deserialize ( - full_object_detection& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version != 1) - throw serialization_error("Unexpected version encountered while deserializing dlib::full_object_detection."); - - deserialize(item.rect, in); - deserialize(item.parts, in); - } - - bool operator==( - const full_object_detection& rhs - ) const - { - if (rect != rhs.rect) - return false; - if (parts.size() != rhs.parts.size()) - return false; - for (size_t i = 0; i < parts.size(); ++i) - { - if (parts[i] != rhs.parts[i]) - return false; - } - return true; - } - - private: - rectangle rect; - std::vector<point> parts; - }; - -// ---------------------------------------------------------------------------------------- - - inline bool all_parts_in_rect ( - const full_object_detection& obj - ) - { - for (unsigned long i = 0; i < obj.num_parts(); ++i) - { - if (obj.get_rect().contains(obj.part(i)) == false && - obj.part(i) != OBJECT_PART_NOT_PRESENT) - return false; - } - return true; - } - -// ---------------------------------------------------------------------------------------- - - struct mmod_rect - { - mmod_rect() = default; - mmod_rect(const rectangle& r) : rect(r) {} - mmod_rect(const rectangle& r, double score) : rect(r),detection_confidence(score) {} - mmod_rect(const rectangle& r, double score, const std::string& label) : rect(r),detection_confidence(score), label(label) {} - - rectangle rect; - double detection_confidence = 0; - bool ignore = false; - std::string label; - - operator rectangle() const { return rect; } - bool operator == (const mmod_rect& rhs) const - { - return rect == rhs.rect - && detection_confidence == rhs.detection_confidence - && ignore == rhs.ignore - && label == rhs.label; - } - }; - - inline mmod_rect ignored_mmod_rect(const rectangle& r) - { - mmod_rect temp(r); - temp.ignore = true; - return temp; - } - - inline void serialize(const mmod_rect& item, std::ostream& out) - { - int version = 2; - serialize(version, out); - serialize(item.rect, out); - serialize(item.detection_confidence, out); - serialize(item.ignore, out); - serialize(item.label, out); - } - - inline void deserialize(mmod_rect& item, std::istream& in) - { - int version = 0; - deserialize(version, in); - if (version != 1 && version != 2) - throw serialization_error("Unexpected version found while deserializing dlib::mmod_rect"); - deserialize(item.rect, in); - deserialize(item.detection_confidence, in); - deserialize(item.ignore, in); - if (version == 2) - deserialize(item.label, in); - else - item.label = ""; - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_FULL_OBJECT_DeTECTION_H_ - diff --git a/ml/dlib/dlib/image_processing/full_object_detection_abstract.h b/ml/dlib/dlib/image_processing/full_object_detection_abstract.h deleted file mode 100644 index 099ee01b0..000000000 --- a/ml/dlib/dlib/image_processing/full_object_detection_abstract.h +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (C) 2012 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_FULL_OBJECT_DeTECTION_ABSTRACT_Hh_ -#ifdef DLIB_FULL_OBJECT_DeTECTION_ABSTRACT_Hh_ - -#include <vector> -#include "../geometry.h" -#include "../serialize.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - const static point OBJECT_PART_NOT_PRESENT(0x7FFFFFFF, - 0x7FFFFFFF); - -// ---------------------------------------------------------------------------------------- - - class full_object_detection - { - /*! - WHAT THIS OBJECT REPRESENTS - This object represents the location of an object in an image along with the - positions of each of its constituent parts. - !*/ - - public: - - full_object_detection( - const rectangle& rect, - const std::vector<point>& parts - ); - /*! - ensures - - #get_rect() == rect - - #num_parts() == parts.size() - - for all valid i: - - part(i) == parts[i] - !*/ - - full_object_detection( - ); - /*! - ensures - - #get_rect().is_empty() == true - - #num_parts() == 0 - !*/ - - explicit full_object_detection( - const rectangle& rect - ); - /*! - ensures - - #get_rect() == rect - - #num_parts() == 0 - !*/ - - const rectangle& get_rect( - ) const; - /*! - ensures - - returns the rectangle that indicates where this object is. In general, - this should be the bounding box for the object. - !*/ - - rectangle& get_rect( - ); - /*! - ensures - - returns the rectangle that indicates where this object is. In general, - this should be the bounding box for the object. - !*/ - - unsigned long num_parts( - ) const; - /*! - ensures - - returns the number of parts in this object. - !*/ - - const point& part( - unsigned long idx - ) const; - /*! - requires - - idx < num_parts() - ensures - - returns the location of the center of the idx-th part of this object. - Note that it is valid for a part to be "not present". This is indicated - when the return value of part() is equal to OBJECT_PART_NOT_PRESENT. - This is useful for modeling object parts that are not always observed. - !*/ - - point& part( - unsigned long idx - ); - /*! - requires - - idx < num_parts() - ensures - - returns the location of the center of the idx-th part of this object. - Note that it is valid for a part to be "not present". This is indicated - when the return value of part() is equal to OBJECT_PART_NOT_PRESENT. - This is useful for modeling object parts that are not always observed. - !*/ - - bool operator==( - const full_object_detection& rhs - ) const; - /*! - ensures - - returns true if and only if *this and rhs have identical state. - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - void serialize ( - const full_object_detection& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - void deserialize ( - full_object_detection& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - - bool all_parts_in_rect ( - const full_object_detection& obj - ); - /*! - ensures - - returns true if all the parts in obj are contained within obj.get_rect(). - That is, returns true if and only if, for all valid i, the following is - always true: - obj.get_rect().contains(obj.part(i)) == true || obj.part(i) == OBJECT_PART_NOT_PRESENT - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - struct mmod_rect - { - /*! - WHAT THIS OBJECT REPRESENTS - This is a simple struct that is used to give training data and receive detections - from the Max-Margin Object Detection loss layer loss_mmod_ object. - !*/ - - mmod_rect() = default; - mmod_rect(const rectangle& r) : rect(r) {} - mmod_rect(const rectangle& r, double score) : rect(r),detection_confidence(score) {} - mmod_rect(const rectangle& r, double score, const std::string& label) : rect(r),detection_confidence(score),label(label) {} - - rectangle rect; - double detection_confidence = 0; - bool ignore = false; - std::string label; - - operator rectangle() const { return rect; } - - bool operator == (const mmod_rect& rhs) const; - /*! - ensures - - returns true if and only if all the elements of this object compare equal - to the corresponding elements of rhs. - !*/ - }; - - mmod_rect ignored_mmod_rect( - const rectangle& r - ); - /*! - ensures - - returns a mmod_rect R such that: - - R.rect == r - - R.ignore == true - - R.detection_confidence == 0 - - R.label == "" - !*/ - - void serialize(const mmod_rect& item, std::ostream& out); - void deserialize(mmod_rect& item, std::istream& in); - /*! - provides serialization support - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_FULL_OBJECT_DeTECTION_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/generic_image.h b/ml/dlib/dlib/image_processing/generic_image.h deleted file mode 100644 index 362277368..000000000 --- a/ml/dlib/dlib/image_processing/generic_image.h +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_GeNERIC_IMAGE_Hh_ -#define DLIB_GeNERIC_IMAGE_Hh_ - -#include "../assert.h" - -namespace dlib -{ - - /*! - In dlib, an "image" is any object that implements the generic image interface. In - particular, this simply means that an image type (let's refer to it as image_type - from here on) has the following seven global functions defined for it: - - long num_rows (const image_type& img) - - long num_columns (const image_type& img) - - void set_image_size( image_type& img, long rows, long cols) - - void* image_data ( image_type& img) - - const void* image_data (const image_type& img) - - long width_step (const image_type& img) - - void swap ( image_type& a, image_type& b) - And also provides a specialization of the image_traits template that looks like: - namespace dlib - { - template <> - struct image_traits<image_type> - { - typedef the_type_of_pixel_used_in_image_type pixel_type; - }; - } - - Additionally, an image object must be default constructable. This means that - expressions of the form: - image_type img; - Must be legal. - - Finally, the type of pixel in image_type must have a pixel_traits specialization. - That is, pixel_traits<typename image_traits<image_type>::pixel_type> must be one of - the specializations of pixel_traits. - - - To be very precise, the seven functions defined above are defined thusly: - - long num_rows( - const image_type& img - ); - /!* - ensures - - returns the number of rows in the given image - *!/ - - long num_columns( - const image_type& img - ); - /!* - ensures - - returns the number of columns in the given image - *!/ - - void set_image_size( - image_type& img, - long rows, - long cols - ); - /!* - requires - - rows >= 0 && cols >= 0 - ensures - - num_rows(#img) == rows - - num_columns(#img) == cols - *!/ - - void* image_data( - image_type& img - ); - /!* - ensures - - returns a non-const pointer to the pixel at row and column position 0,0 - in the given image. Or if the image has zero rows or columns in it - then this function returns NULL. - - The image lays pixels down in row major order. However, there might - be padding at the end of each row. The amount of padding is given by - width_step(img). - *!/ - - const void* image_data( - const image_type& img - ); - /!* - ensures - - returns a const pointer to the pixel at row and column position 0,0 in - the given image. Or if the image has zero rows or columns in it then - this function returns NULL. - - The image lays pixels down in row major order. However, there might - be padding at the end of each row. The amount of padding is given by - width_step(img). - *!/ - - long width_step( - const image_type& img - ); - /!* - ensures - - returns the size of one row of the image, in bytes. More precisely, - return a number N such that: (char*)image_data(img) + N*R == a - pointer to the first pixel in the R-th row of the image. This means - that the image must lay its pixels down in row major order. - *!/ - - void swap( - image_type& a, - image_type& b - ); - /!* - ensures - - swaps the state of a and b - *!/ - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename image_type> - struct image_traits; - /*! - WHAT THIS OBJECT REPRESENTS - This is a traits class for generic image objects. You can use it to find out - the pixel type contained within an image via an expression of the form: - image_traits<image_type>::pixel_type - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// UTILITIES TO MAKE ACCESSING IMAGE PIXELS SIMPLER -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_type - > - class image_view - { - /*! - REQUIREMENTS ON image_type - image_type must be an image object as defined at the top of this file. - - WHAT THIS OBJECT REPRESENTS - This object takes an image object and wraps it with an interface that makes - it look like a dlib::array2d. That is, it makes it look similar to a - regular 2-dimensional C style array, making code which operates on the - pixels simple to read. - - Note that an image_view instance is valid until the image given to its - constructor is modified through an interface other than the image_view - instance. This is because, for example, someone might cause the underlying - image object to reallocate its memory, thus invalidating the pointer to its - pixel data stored in the image_view. - - As an side, the reason why this object stores a pointer to the image - object's data and uses that pointer instead of calling image_data() each - time a pixel is accessed is to allow for image objects to implement - complex, and possibly slow, image_data() functions. For example, an image - object might perform some kind of synchronization between a GPU and the - host memory during a call to image_data(). Therefore, we call image_data() - only in image_view's constructor to avoid the performance penalty of - calling it for each pixel access. - !*/ - - public: - typedef typename image_traits<image_type>::pixel_type pixel_type; - - image_view( - image_type& img - ) : - _data((char*)image_data(img)), - _width_step(width_step(img)), - _nr(num_rows(img)), - _nc(num_columns(img)), - _img(&img) - {} - - long nr() const { return _nr; } - /*! - ensures - - returns the number of rows in this image. - !*/ - - long nc() const { return _nc; } - /*! - ensures - - returns the number of columns in this image. - !*/ - - unsigned long size() const { return static_cast<unsigned long>(nr()*nc()); } - /*! - ensures - - returns the number of pixels in this image. - !*/ - -#ifndef ENABLE_ASSERTS - pixel_type* operator[] (long row) { return (pixel_type*)(_data+_width_step*row); } - /*! - requires - - 0 <= row < nr() - ensures - - returns a pointer to the first pixel in the row-th row. Therefore, the - pixel at row and column position r,c can be accessed via (*this)[r][c]. - !*/ - - const pixel_type* operator[] (long row) const { return (const pixel_type*)(_data+_width_step*row); } - /*! - requires - - 0 <= row < nr() - ensures - - returns a const pointer to the first pixel in the row-th row. Therefore, - the pixel at row and column position r,c can be accessed via - (*this)[r][c]. - !*/ -#else - // If asserts are enabled then we need to return a proxy class so we can make sure - // the column accesses don't go out of bounds. - struct pix_row - { - pix_row(pixel_type* data_, long nc_) : data(data_),_nc(nc_) {} - const pixel_type& operator[] (long col) const - { - DLIB_ASSERT(0 <= col && col < _nc, - "\t The given column index is out of range." - << "\n\t col: " << col - << "\n\t _nc: " << _nc); - return data[col]; - } - pixel_type& operator[] (long col) - { - DLIB_ASSERT(0 <= col && col < _nc, - "\t The given column index is out of range." - << "\n\t col: " << col - << "\n\t _nc: " << _nc); - return data[col]; - } - private: - pixel_type* const data; - const long _nc; - }; - pix_row operator[] (long row) - { - DLIB_ASSERT(0 <= row && row < _nr, - "\t The given row index is out of range." - << "\n\t row: " << row - << "\n\t _nr: " << _nr); - return pix_row((pixel_type*)(_data+_width_step*row), _nc); - } - const pix_row operator[] (long row) const - { - DLIB_ASSERT(0 <= row && row < _nr, - "\t The given row index is out of range." - << "\n\t row: " << row - << "\n\t _nr: " << _nr); - return pix_row((pixel_type*)(_data+_width_step*row), _nc); - } -#endif - - void set_size(long rows, long cols) - /*! - requires - - rows >= 0 && cols >= 0 - ensures - - Tells the underlying image to resize itself to have the given number of - rows and columns. - - #nr() == rows - - #nc() == cols - !*/ - { - DLIB_ASSERT((cols >= 0 && rows >= 0), - "\t image_view::set_size(long rows, long cols)" - << "\n\t The images can't have negative rows or columns." - << "\n\t cols: " << cols - << "\n\t rows: " << rows - ); - set_image_size(*_img, rows, cols); *this = *_img; - } - - void clear() { set_size(0,0); } - /*! - ensures - - sets the image to have 0 pixels in it. - !*/ - - private: - - char* _data; - long _width_step; - long _nr; - long _nc; - image_type* _img; - }; - -// ---------------------------------------------------------------------------------------- - - template <typename image_type> - class const_image_view - { - /*! - REQUIREMENTS ON image_type - image_type must be an image object as defined at the top of this file. - - WHAT THIS OBJECT REPRESENTS - This object is just like the image_view except that it provides a "const" - view into an image. That is, it has the same interface as image_view - except that you can't modify the image through a const_image_view. - !*/ - - public: - typedef typename image_traits<image_type>::pixel_type pixel_type; - - const_image_view( - const image_type& img - ) : - _data((char*)image_data(img)), - _width_step(width_step(img)), - _nr(num_rows(img)), - _nc(num_columns(img)) - {} - - long nr() const { return _nr; } - long nc() const { return _nc; } - unsigned long size() const { return static_cast<unsigned long>(nr()*nc()); } -#ifndef ENABLE_ASSERTS - const pixel_type* operator[] (long row) const { return (const pixel_type*)(_data+_width_step*row); } -#else - // If asserts are enabled then we need to return a proxy class so we can make sure - // the column accesses don't go out of bounds. - struct pix_row - { - pix_row(pixel_type* data_, long nc_) : data(data_),_nc(nc_) {} - const pixel_type& operator[] (long col) const - { - DLIB_ASSERT(0 <= col && col < _nc, - "\t The given column index is out of range." - << "\n\t col: " << col - << "\n\t _nc: " << _nc); - return data[col]; - } - private: - pixel_type* const data; - const long _nc; - }; - const pix_row operator[] (long row) const - { - DLIB_ASSERT(0 <= row && row < _nr, - "\t The given row index is out of range." - << "\n\t row: " << row - << "\n\t _nr: " << _nr); - return pix_row((pixel_type*)(_data+_width_step*row), _nc); - } -#endif - - private: - const char* _data; - long _width_step; - long _nr; - long _nc; - }; - -// ---------------------------------------------------------------------------------------- - - template <typename image_type> - image_view<image_type> make_image_view ( image_type& img) - { return image_view<image_type>(img); } - /*! - requires - - image_type == an image object that implements the interface defined at the - top of this file. - ensures - - constructs an image_view from an image object - !*/ - - template <typename image_type> - const_image_view<image_type> make_image_view (const image_type& img) - { return const_image_view<image_type>(img); } - /*! - requires - - image_type == an image object that implements the interface defined at the - top of this file. - ensures - - constructs a const_image_view from an image object - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename image_type> - inline unsigned long image_size( - const image_type& img - ) { return num_columns(img)*num_rows(img); } - /*! - requires - - image_type == an image object that implements the interface defined at the - top of this file. - ensures - - returns the number of pixels in the given image. - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename image_type> - inline long num_rows( - const image_type& img - ) { return img.nr(); } - /*! - ensures - - By default, try to use the member function .nr() to determine the number - of rows in an image. However, as stated at the top of this file, image - objects should provide their own overload of num_rows() if needed. - !*/ - - template <typename image_type> - inline long num_columns( - const image_type& img - ) { return img.nc(); } - /*! - ensures - - By default, try to use the member function .nc() to determine the number - of columns in an image. However, as stated at the top of this file, image - objects should provide their own overload of num_rows() if needed. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_GeNERIC_IMAGE_Hh_ - diff --git a/ml/dlib/dlib/image_processing/object_detector.h b/ml/dlib/dlib/image_processing/object_detector.h deleted file mode 100644 index 9f78abd19..000000000 --- a/ml/dlib/dlib/image_processing/object_detector.h +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_OBJECT_DeTECTOR_Hh_ -#define DLIB_OBJECT_DeTECTOR_Hh_ - -#include "object_detector_abstract.h" -#include "../geometry.h" -#include <vector> -#include "box_overlap_testing.h" -#include "full_object_detection.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - struct rect_detection - { - double detection_confidence; - unsigned long weight_index; - rectangle rect; - - bool operator<(const rect_detection& item) const { return detection_confidence < item.detection_confidence; } - }; - - struct full_detection - { - double detection_confidence; - unsigned long weight_index; - full_object_detection rect; - - bool operator<(const full_detection& item) const { return detection_confidence < item.detection_confidence; } - }; - -// ---------------------------------------------------------------------------------------- - - template <typename image_scanner_type> - struct processed_weight_vector - { - processed_weight_vector(){} - - typedef typename image_scanner_type::feature_vector_type feature_vector_type; - - void init ( - const image_scanner_type& - ) - /*! - requires - - w has already been assigned its value. Note that the point of this - function is to allow an image scanner to overload the - processed_weight_vector template and provide some different kind of - object as the output of get_detect_argument(). For example, the - scan_fhog_pyramid object uses an overload that causes - get_detect_argument() to return the special fhog_filterbank object - instead of a feature_vector_type. This avoids needing to construct the - fhog_filterbank during each call to detect and therefore speeds up - detection. - !*/ - {} - - // return the first argument to image_scanner_type::detect() - const feature_vector_type& get_detect_argument() const { return w; } - - feature_vector_type w; - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type_ - > - class object_detector - { - public: - typedef image_scanner_type_ image_scanner_type; - typedef typename image_scanner_type::feature_vector_type feature_vector_type; - - object_detector ( - ); - - object_detector ( - const object_detector& item - ); - - object_detector ( - const image_scanner_type& scanner_, - const test_box_overlap& overlap_tester_, - const feature_vector_type& w_ - ); - - object_detector ( - const image_scanner_type& scanner_, - const test_box_overlap& overlap_tester_, - const std::vector<feature_vector_type>& w_ - ); - - explicit object_detector ( - const std::vector<object_detector>& detectors - ); - - unsigned long num_detectors ( - ) const { return w.size(); } - - const feature_vector_type& get_w ( - unsigned long idx = 0 - ) const { return w[idx].w; } - - const processed_weight_vector<image_scanner_type>& get_processed_w ( - unsigned long idx = 0 - ) const { return w[idx]; } - - const test_box_overlap& get_overlap_tester ( - ) const; - - const image_scanner_type& get_scanner ( - ) const; - - object_detector& operator= ( - const object_detector& item - ); - - template < - typename image_type - > - std::vector<rectangle> operator() ( - const image_type& img, - double adjust_threshold = 0 - ); - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<std::pair<double, rectangle> >& final_dets, - double adjust_threshold = 0 - ); - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<std::pair<double, full_object_detection> >& final_dets, - double adjust_threshold = 0 - ); - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<full_object_detection>& final_dets, - double adjust_threshold = 0 - ); - - // These typedefs are here for backwards compatibility with previous versions of - // dlib. - typedef ::dlib::rect_detection rect_detection; - typedef ::dlib::full_detection full_detection; - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<rect_detection>& final_dets, - double adjust_threshold = 0 - ); - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<full_detection>& final_dets, - double adjust_threshold = 0 - ); - - template <typename T> - friend void serialize ( - const object_detector<T>& item, - std::ostream& out - ); - - template <typename T> - friend void deserialize ( - object_detector<T>& item, - std::istream& in - ); - - private: - - bool overlaps_any_box ( - const std::vector<rect_detection>& rects, - const dlib::rectangle& rect - ) const - { - for (unsigned long i = 0; i < rects.size(); ++i) - { - if (boxes_overlap(rects[i].rect, rect)) - return true; - } - return false; - } - - test_box_overlap boxes_overlap; - std::vector<processed_weight_vector<image_scanner_type> > w; - image_scanner_type scanner; - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void serialize ( - const object_detector<T>& item, - std::ostream& out - ) - { - int version = 2; - serialize(version, out); - - T scanner; - scanner.copy_configuration(item.scanner); - serialize(scanner, out); - serialize(item.boxes_overlap, out); - // serialize all the weight vectors - serialize(item.w.size(), out); - for (unsigned long i = 0; i < item.w.size(); ++i) - serialize(item.w[i].w, out); - } - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void deserialize ( - object_detector<T>& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version == 1) - { - deserialize(item.scanner, in); - item.w.resize(1); - deserialize(item.w[0].w, in); - item.w[0].init(item.scanner); - deserialize(item.boxes_overlap, in); - } - else if (version == 2) - { - deserialize(item.scanner, in); - deserialize(item.boxes_overlap, in); - unsigned long num_detectors = 0; - deserialize(num_detectors, in); - item.w.resize(num_detectors); - for (unsigned long i = 0; i < item.w.size(); ++i) - { - deserialize(item.w[i].w, in); - item.w[i].init(item.scanner); - } - } - else - { - throw serialization_error("Unexpected version encountered while deserializing a dlib::object_detector object."); - } - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// object_detector member functions -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>:: - object_detector ( - ) - { - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>:: - object_detector ( - const object_detector& item - ) - { - boxes_overlap = item.boxes_overlap; - w = item.w; - scanner.copy_configuration(item.scanner); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>:: - object_detector ( - const image_scanner_type& scanner_, - const test_box_overlap& overlap_tester, - const feature_vector_type& w_ - ) : - boxes_overlap(overlap_tester) - { - // make sure requires clause is not broken - DLIB_ASSERT(scanner_.get_num_detection_templates() > 0 && - w_.size() == scanner_.get_num_dimensions() + 1, - "\t object_detector::object_detector(scanner_,overlap_tester,w_)" - << "\n\t Invalid inputs were given to this function " - << "\n\t scanner_.get_num_detection_templates(): " << scanner_.get_num_detection_templates() - << "\n\t w_.size(): " << w_.size() - << "\n\t scanner_.get_num_dimensions(): " << scanner_.get_num_dimensions() - << "\n\t this: " << this - ); - - scanner.copy_configuration(scanner_); - w.resize(1); - w[0].w = w_; - w[0].init(scanner); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>:: - object_detector ( - const image_scanner_type& scanner_, - const test_box_overlap& overlap_tester, - const std::vector<feature_vector_type>& w_ - ) : - boxes_overlap(overlap_tester) - { - // make sure requires clause is not broken - DLIB_CASSERT(scanner_.get_num_detection_templates() > 0 && w_.size() > 0, - "\t object_detector::object_detector(scanner_,overlap_tester,w_)" - << "\n\t Invalid inputs were given to this function " - << "\n\t scanner_.get_num_detection_templates(): " << scanner_.get_num_detection_templates() - << "\n\t w_.size(): " << w_.size() - << "\n\t this: " << this - ); - - for (unsigned long i = 0; i < w_.size(); ++i) - { - DLIB_CASSERT(w_[i].size() == scanner_.get_num_dimensions() + 1, - "\t object_detector::object_detector(scanner_,overlap_tester,w_)" - << "\n\t Invalid inputs were given to this function " - << "\n\t scanner_.get_num_detection_templates(): " << scanner_.get_num_detection_templates() - << "\n\t w_["<<i<<"].size(): " << w_[i].size() - << "\n\t scanner_.get_num_dimensions(): " << scanner_.get_num_dimensions() - << "\n\t this: " << this - ); - } - - scanner.copy_configuration(scanner_); - w.resize(w_.size()); - for (unsigned long i = 0; i < w.size(); ++i) - { - w[i].w = w_[i]; - w[i].init(scanner); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>:: - object_detector ( - const std::vector<object_detector>& detectors - ) - { - DLIB_CASSERT(detectors.size() != 0, - "\t object_detector::object_detector(detectors)" - << "\n\t Invalid inputs were given to this function " - << "\n\t this: " << this - ); - std::vector<feature_vector_type> weights; - weights.reserve(detectors.size()); - for (unsigned long i = 0; i < detectors.size(); ++i) - { - for (unsigned long j = 0; j < detectors[i].num_detectors(); ++j) - weights.push_back(detectors[i].get_w(j)); - } - - *this = object_detector(detectors[0].get_scanner(), detectors[0].get_overlap_tester(), weights); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - object_detector<image_scanner_type>& object_detector<image_scanner_type>:: - operator= ( - const object_detector& item - ) - { - if (this == &item) - return *this; - - boxes_overlap = item.boxes_overlap; - w = item.w; - scanner.copy_configuration(item.scanner); - return *this; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - void object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - std::vector<rect_detection>& final_dets, - double adjust_threshold - ) - { - scanner.load(img); - std::vector<std::pair<double, rectangle> > dets; - std::vector<rect_detection> dets_accum; - for (unsigned long i = 0; i < w.size(); ++i) - { - const double thresh = w[i].w(scanner.get_num_dimensions()); - scanner.detect(w[i].get_detect_argument(), dets, thresh + adjust_threshold); - for (unsigned long j = 0; j < dets.size(); ++j) - { - rect_detection temp; - temp.detection_confidence = dets[j].first-thresh; - temp.weight_index = i; - temp.rect = dets[j].second; - dets_accum.push_back(temp); - } - } - - // Do non-max suppression - final_dets.clear(); - if (w.size() > 1) - std::sort(dets_accum.rbegin(), dets_accum.rend()); - for (unsigned long i = 0; i < dets_accum.size(); ++i) - { - if (overlaps_any_box(final_dets, dets_accum[i].rect)) - continue; - - final_dets.push_back(dets_accum[i]); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - void object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - std::vector<full_detection>& final_dets, - double adjust_threshold - ) - { - std::vector<rect_detection> dets; - (*this)(img,dets,adjust_threshold); - - final_dets.resize(dets.size()); - - // convert all the rectangle detections into full_object_detections. - for (unsigned long i = 0; i < dets.size(); ++i) - { - final_dets[i].detection_confidence = dets[i].detection_confidence; - final_dets[i].weight_index = dets[i].weight_index; - final_dets[i].rect = scanner.get_full_object_detection(dets[i].rect, w[dets[i].weight_index].w); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - std::vector<rectangle> object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - double adjust_threshold - ) - { - std::vector<rect_detection> dets; - (*this)(img,dets,adjust_threshold); - - std::vector<rectangle> final_dets(dets.size()); - for (unsigned long i = 0; i < dets.size(); ++i) - final_dets[i] = dets[i].rect; - - return final_dets; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - void object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - std::vector<std::pair<double, rectangle> >& final_dets, - double adjust_threshold - ) - { - std::vector<rect_detection> dets; - (*this)(img,dets,adjust_threshold); - - final_dets.resize(dets.size()); - for (unsigned long i = 0; i < dets.size(); ++i) - final_dets[i] = std::make_pair(dets[i].detection_confidence,dets[i].rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - void object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - std::vector<std::pair<double, full_object_detection> >& final_dets, - double adjust_threshold - ) - { - std::vector<rect_detection> dets; - (*this)(img,dets,adjust_threshold); - - final_dets.clear(); - final_dets.reserve(dets.size()); - - // convert all the rectangle detections into full_object_detections. - for (unsigned long i = 0; i < dets.size(); ++i) - { - final_dets.push_back(std::make_pair(dets[i].detection_confidence, - scanner.get_full_object_detection(dets[i].rect, w[dets[i].weight_index].w))); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - template < - typename image_type - > - void object_detector<image_scanner_type>:: - operator() ( - const image_type& img, - std::vector<full_object_detection>& final_dets, - double adjust_threshold - ) - { - std::vector<rect_detection> dets; - (*this)(img,dets,adjust_threshold); - - final_dets.clear(); - final_dets.reserve(dets.size()); - - // convert all the rectangle detections into full_object_detections. - for (unsigned long i = 0; i < dets.size(); ++i) - { - final_dets.push_back(scanner.get_full_object_detection(dets[i].rect, w[dets[i].weight_index].w)); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - const test_box_overlap& object_detector<image_scanner_type>:: - get_overlap_tester ( - ) const - { - return boxes_overlap; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - const image_scanner_type& object_detector<image_scanner_type>:: - get_scanner ( - ) const - { - return scanner; - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_OBJECT_DeTECTOR_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/object_detector_abstract.h b/ml/dlib/dlib/image_processing/object_detector_abstract.h deleted file mode 100644 index 9578d8b03..000000000 --- a/ml/dlib/dlib/image_processing/object_detector_abstract.h +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_OBJECT_DeTECTOR_ABSTRACT_Hh_ -#ifdef DLIB_OBJECT_DeTECTOR_ABSTRACT_Hh_ - -#include "../geometry.h" -#include <vector> -#include "box_overlap_testing_abstract.h" -#include "full_object_detection_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - struct rect_detection - { - double detection_confidence; - unsigned long weight_index; - rectangle rect; - }; - - struct full_detection - { - double detection_confidence; - unsigned long weight_index; - full_object_detection rect; - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type_ - > - class object_detector - { - /*! - REQUIREMENTS ON image_scanner_type_ - image_scanner_type_ must be an implementation of - dlib/image_processing/scan_image_pyramid_abstract.h or - dlib/image_processing/scan_fhog_pyramid.h or - dlib/image_processing/scan_image_custom.h or - dlib/image_processing/scan_image_boxes_abstract.h - - WHAT THIS OBJECT REPRESENTS - This object is a tool for detecting the positions of objects in an image. - In particular, it is a simple container to aggregate an instance of an image - scanner (i.e. scan_image_pyramid, scan_fhog_pyramid, scan_image_custom, or - scan_image_boxes), the weight vector needed by one of these image scanners, - and finally an instance of test_box_overlap. The test_box_overlap object - is used to perform non-max suppression on the output of the image scanner - object. - - Note further that this object can contain multiple weight vectors. In this - case, it will run the image scanner multiple times, once with each of the - weight vectors. Then it will aggregate the results from all runs, perform - non-max suppression and then return the results. Therefore, the object_detector - can also be used as a container for a set of object detectors that all use - the same image scanner but different weight vectors. This is useful since - the object detection procedure has two parts. A loading step where the - image is loaded into the scanner, then a detect step which uses the weight - vector to locate objects in the image. Since the loading step is independent - of the weight vector it is most efficient to run multiple detectors by - performing one load into a scanner followed by multiple detect steps. This - avoids unnecessarily loading the same image into the scanner multiple times. - !*/ - public: - typedef image_scanner_type_ image_scanner_type; - typedef typename image_scanner_type::feature_vector_type feature_vector_type; - - object_detector ( - ); - /*! - ensures - - This detector won't generate any detections when - presented with an image. - - #num_detectors() == 0 - !*/ - - object_detector ( - const object_detector& item - ); - /*! - ensures - - #*this is a copy of item - - #get_scanner() == item.get_scanner() - (note that only the "configuration" of item.get_scanner() is copied. - I.e. the copy is done using copy_configuration()) - !*/ - - object_detector ( - const image_scanner_type& scanner, - const test_box_overlap& overlap_tester, - const feature_vector_type& w - ); - /*! - requires - - w.size() == scanner.get_num_dimensions() + 1 - - scanner.get_num_detection_templates() > 0 - ensures - - When the operator() member function is called it will - invoke scanner.detect(w,dets,w(w.size()-1)), suppress - overlapping detections, and then report the results. - - when #*this is used to detect objects, the set of - output detections will never contain any overlaps - with respect to overlap_tester. That is, for all - pairs of returned detections A and B, we will always - have: overlap_tester(A,B) == false - - #get_w() == w - - #get_overlap_tester() == overlap_tester - - #get_scanner() == scanner - (note that only the "configuration" of scanner is copied. - I.e. the copy is done using copy_configuration()) - - #num_detectors() == 1 - !*/ - - object_detector ( - const image_scanner_type& scanner, - const test_box_overlap& overlap_tester, - const std::vector<feature_vector_type>& w - ); - /*! - requires - - for all valid i: - - w[i].size() == scanner.get_num_dimensions() + 1 - - scanner.get_num_detection_templates() > 0 - - w.size() > 0 - ensures - - When the operator() member function is called it will invoke - get_scanner().detect(w[i],dets,w[i](w[i].size()-1)) for all valid i. Then it - will take all the detections output by the calls to detect() and suppress - overlapping detections, and finally report the results. - - when #*this is used to detect objects, the set of output detections will - never contain any overlaps with respect to overlap_tester. That is, for - all pairs of returned detections A and B, we will always have: - overlap_tester(A,B) == false - - for all valid i: - - #get_w(i) == w[i] - - #num_detectors() == w.size() - - #get_overlap_tester() == overlap_tester - - #get_scanner() == scanner - (note that only the "configuration" of scanner is copied. - I.e. the copy is done using copy_configuration()) - !*/ - - explicit object_detector ( - const std::vector<object_detector>& detectors - ); - /*! - requires - - detectors.size() != 0 - - All the detectors must use compatibly configured scanners. That is, it - must make sense for the weight vector from one detector to be used with - the scanner from any other. - - for all valid i: - - detectors[i].get_scanner().get_num_dimensions() == detectors[0].get_scanner().get_num_dimensions() - (i.e. all the detectors use scanners that use the same kind of feature vectors.) - ensures - - Very much like the above constructor, this constructor takes all the - given detectors and packs them into #*this. That is, invoking operator() - on #*this will run all the detectors, perform non-max suppression, and - then report the results. - - When #*this is used to detect objects, the set of output detections will - never contain any overlaps with respect to overlap_tester. That is, for - all pairs of returned detections A and B, we will always have: - overlap_tester(A,B) == false - - #num_detectors() == The sum of detectors[i].num_detectors() for all valid i. - - #get_overlap_tester() == detectors[0].get_overlap_tester() - - #get_scanner() == detectors[0].get_scanner() - (note that only the "configuration" of scanner is copied. I.e. the copy - is done using copy_configuration()) - !*/ - - unsigned long num_detectors ( - ) const; - /*! - ensures - - returns the number of weight vectors in this object. Since each weight - vector logically represents an object detector, this returns the number - of object detectors contained in this object. - !*/ - - const feature_vector_type& get_w ( - unsigned long idx = 0 - ) const; - /*! - requires - - idx < num_detectors() - ensures - - returns the idx-th weight vector loaded into this object. All the weight vectors - have the same dimension and logically each represents a different detector. - !*/ - - const test_box_overlap& get_overlap_tester ( - ) const; - /*! - ensures - - returns the overlap tester used by this object - !*/ - - const image_scanner_type& get_scanner ( - ) const; - /*! - ensures - - returns the image scanner used by this object. - !*/ - - object_detector& operator= ( - const object_detector& item - ); - /*! - ensures - - #*this is a copy of item - - #get_scanner() == item.get_scanner() - (note that only the "configuration" of item.get_scanner() is - copied. I.e. the copy is done using copy_configuration()) - - returns #*this - !*/ - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<rect_detection>& dets, - double adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - Performs object detection on the given image and stores the detected - objects into #dets. In particular, we will have that: - - #dets is sorted such that the highest confidence detections come - first. E.g. element 0 is the best detection, element 1 the next - best, and so on. - - #dets.size() == the number of detected objects. - - #dets[i].detection_confidence == The strength of the i-th detection. - Larger values indicate that the detector is more confident that - #dets[i] is a correct detection rather than being a false alarm. - Moreover, the detection_confidence is equal to the detection value - output by the scanner minus the threshold value stored at the end of - the weight vector in get_w(#dets[i].weight_index). - - #dets[i].weight_index == the index for the weight vector that - generated this detection. - - #dets[i].rect == the bounding box for the i-th detection. - - #get_scanner() will have been loaded with img. Therefore, you can call - #get_scanner().get_feature_vector() to obtain the feature vectors or - #get_scanner().get_full_object_detection() to get the - full_object_detections for the resulting object detection boxes. - - The detection threshold is adjusted by having adjust_threshold added to - it. Therefore, an adjust_threshold value > 0 makes detecting objects - harder while a negative value makes it easier. Moreover, the following - will be true for all valid i: - - #dets[i].detection_confidence >= adjust_threshold - This means that, for example, you can obtain the maximum possible number - of detections by setting adjust_threshold equal to negative infinity. - !*/ - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<full_detection>& dets, - double adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - This function is identical to the above operator() routine, except that - it outputs full_object_detections instead of rectangles. This means that - the output includes part locations. In particular, calling this function - is the same as calling the above operator() routine and then using - get_scanner().get_full_object_detection() to resolve all the rectangles - into full_object_detections. Therefore, this version of operator() is - simply a convenience function for performing this set of operations. - !*/ - - template < - typename image_type - > - std::vector<rectangle> operator() ( - const image_type& img, - const adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - This function is identical to the above operator() routine, except that - it returns a std::vector<rectangle> which contains just the bounding - boxes of all the detections. - !*/ - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<std::pair<double, rectangle> >& dets, - double adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - performs object detection on the given image and stores the - detected objects into #dets. In particular, we will have that: - - #dets is sorted such that the highest confidence detections - come first. E.g. element 0 is the best detection, element 1 - the next best, and so on. - - #dets.size() == the number of detected objects. - - #dets[i].first gives the "detection confidence", of the i-th - detection. This is the detection value output by the scanner minus - the threshold value stored at the end of the weight vector in get_w(). - - #dets[i].second == the bounding box for the i-th detection. - - #get_scanner() will have been loaded with img. Therefore, you can call - #get_scanner().get_feature_vector() to obtain the feature vectors or - #get_scanner().get_full_object_detection() to get the - full_object_detections for the resulting object detection boxes. - - The detection threshold is adjusted by having adjust_threshold added to - it. Therefore, an adjust_threshold value > 0 makes detecting objects - harder while a negative value makes it easier. Moreover, the following - will be true for all valid i: - - #dets[i].first >= adjust_threshold - This means that, for example, you can obtain the maximum possible number - of detections by setting adjust_threshold equal to negative infinity. - !*/ - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<std::pair<double, full_object_detection> >& dets, - double adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - This function is identical to the above operator() routine, except that - it outputs full_object_detections instead of rectangles. This means that - the output includes part locations. In particular, calling this function - is the same as calling the above operator() routine and then using - get_scanner().get_full_object_detection() to resolve all the rectangles - into full_object_detections. Therefore, this version of operator() is - simply a convenience function for performing this set of operations. - !*/ - - template < - typename image_type - > - void operator() ( - const image_type& img, - std::vector<full_object_detection>& dets, - double adjust_threshold = 0 - ); - /*! - requires - - img == an object which can be accepted by image_scanner_type::load() - ensures - - This function is identical to the above operator() routine, except that - it doesn't include a double valued score. That is, it just outputs the - full_object_detections. - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void serialize ( - const object_detector<T>& item, - std::ostream& out - ); - /*! - provides serialization support. Note that this function only saves the - configuration part of item.get_scanner(). That is, we use the scanner's - copy_configuration() function to get a copy of the scanner that doesn't contain any - loaded image data and we then save just the configuration part of the scanner. - This means that any serialized object_detectors won't remember any images they have - processed but will otherwise contain all their state and be able to detect objects - in new images. - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void deserialize ( - object_detector<T>& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_OBJECT_DeTECTOR_ABSTRACT_Hh_ - diff --git a/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles.h b/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles.h deleted file mode 100644 index 95ab4f353..000000000 --- a/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles.h +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_Hh_ -#define DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_Hh_ - -#include "remove_unobtainable_rectangles_abstract.h" -#include "scan_image_pyramid.h" -#include "scan_image_boxes.h" -#include "scan_image_custom.h" -#include "scan_fhog_pyramid.h" -#include "../svm/structural_object_detection_trainer.h" -#include "../geometry.h" - - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - inline bool matches_rect ( - const std::vector<rectangle>& rects, - const rectangle& rect, - const double eps - ) - { - for (unsigned long i = 0; i < rects.size(); ++i) - { - const double score = (rect.intersect(rects[i])).area()/(double)(rect+rects[i]).area(); - if (score > eps) - return true; - } - - return false; - } - - inline rectangle get_best_matching_rect ( - const std::vector<rectangle>& rects, - const rectangle& rect - ) - { - double best_score = -1; - rectangle best_rect; - for (unsigned long i = 0; i < rects.size(); ++i) - { - const double score = (rect.intersect(rects[i])).area()/(double)(rect+rects[i]).area(); - if (score > best_score) - { - best_score = score; - best_rect = rects[i]; - } - } - return best_rect; - } - - // ------------------------------------------------------------------------------------ - - template < - typename image_array_type, - typename image_scanner_type - > - std::vector<std::vector<rectangle> > pyramid_remove_unobtainable_rectangles ( - const structural_object_detection_trainer<image_scanner_type>& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - using namespace dlib::impl; - // make sure requires clause is not broken - DLIB_ASSERT(images.size() == object_locations.size(), - "\t std::vector<std::vector<rectangle>> remove_unobtainable_rectangles()" - << "\n\t Invalid inputs were given to this function." - ); - - - std::vector<std::vector<rectangle> > rejects(images.size()); - - // If the trainer is setup to automatically fit the overlap tester to the data then - // we should use the loosest possible overlap tester here. Otherwise we should use - // the tester the trainer will use. - test_box_overlap boxes_overlap(0.9999999,1); - if (!trainer.auto_set_overlap_tester()) - boxes_overlap = trainer.get_overlap_tester(); - - for (unsigned long k = 0; k < images.size(); ++k) - { - std::vector<rectangle> objs = object_locations[k]; - - // First remove things that don't have any matches with the candidate object - // locations. - std::vector<rectangle> good_rects; - for (unsigned long j = 0; j < objs.size(); ++j) - { - const rectangle rect = trainer.get_scanner().get_best_matching_rect(objs[j]); - const double score = (objs[j].intersect(rect)).area()/(double)(objs[j] + rect).area(); - if (score > trainer.get_match_eps()) - good_rects.push_back(objs[j]); - else - rejects[k].push_back(objs[j]); - } - object_locations[k] = good_rects; - - - // Remap these rectangles to the ones that can come out of the scanner. That - // way when we compare them to each other in the following loop we will know if - // any distinct truth rectangles get mapped to overlapping boxes. - objs.resize(good_rects.size()); - for (unsigned long i = 0; i < good_rects.size(); ++i) - objs[i] = trainer.get_scanner().get_best_matching_rect(good_rects[i]); - - good_rects.clear(); - // now check for truth rects that are too close together. - for (unsigned long i = 0; i < objs.size(); ++i) - { - // check if objs[i] hits another box - bool hit_box = false; - for (unsigned long j = i+1; j < objs.size(); ++j) - { - if (boxes_overlap(objs[i], objs[j])) - { - hit_box = true; - break; - } - } - if (hit_box) - rejects[k].push_back(object_locations[k][i]); - else - good_rects.push_back(object_locations[k][i]); - } - object_locations[k] = good_rects; - } - - return rejects; - } - - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type, - typename Pyramid_type, - typename Feature_extractor_type - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - const structural_object_detection_trainer<scan_image_pyramid<Pyramid_type, Feature_extractor_type> >& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - return impl::pyramid_remove_unobtainable_rectangles(trainer, images, object_locations); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type, - typename Pyramid_type, - typename Feature_extractor_type - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - const structural_object_detection_trainer<scan_fhog_pyramid<Pyramid_type,Feature_extractor_type> >& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - return impl::pyramid_remove_unobtainable_rectangles(trainer, images, object_locations); - } - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - template < - typename image_array_type, - typename scanner_type, - typename get_boxes_functor - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - get_boxes_functor& bg, - const structural_object_detection_trainer<scanner_type>& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - using namespace dlib::impl; - // make sure requires clause is not broken - DLIB_ASSERT(images.size() == object_locations.size(), - "\t std::vector<std::vector<rectangle>> remove_unobtainable_rectangles()" - << "\n\t Invalid inputs were given to this function." - ); - - std::vector<rectangle> rects; - - std::vector<std::vector<rectangle> > rejects(images.size()); - - // If the trainer is setup to automatically fit the overlap tester to the data then - // we should use the loosest possible overlap tester here. Otherwise we should use - // the tester the trainer will use. - test_box_overlap boxes_overlap(0.9999999,1); - if (!trainer.auto_set_overlap_tester()) - boxes_overlap = trainer.get_overlap_tester(); - - for (unsigned long k = 0; k < images.size(); ++k) - { - std::vector<rectangle> objs = object_locations[k]; - // Don't even bother computing the candidate rectangles if there aren't any - // object locations for this image since there isn't anything to do anyway. - if (objs.size() == 0) - continue; - - bg(images[k], rects); - - - // First remove things that don't have any matches with the candidate object - // locations. - std::vector<rectangle> good_rects; - for (unsigned long j = 0; j < objs.size(); ++j) - { - if (matches_rect(rects, objs[j], trainer.get_match_eps())) - good_rects.push_back(objs[j]); - else - rejects[k].push_back(objs[j]); - } - object_locations[k] = good_rects; - - - // Remap these rectangles to the ones that can come out of the scanner. That - // way when we compare them to each other in the following loop we will know if - // any distinct truth rectangles get mapped to overlapping boxes. - objs.resize(good_rects.size()); - for (unsigned long i = 0; i < good_rects.size(); ++i) - objs[i] = get_best_matching_rect(rects, good_rects[i]); - - good_rects.clear(); - // now check for truth rects that are too close together. - for (unsigned long i = 0; i < objs.size(); ++i) - { - // check if objs[i] hits another box - bool hit_box = false; - for (unsigned long j = i+1; j < objs.size(); ++j) - { - if (boxes_overlap(objs[i], objs[j])) - { - hit_box = true; - break; - } - } - if (hit_box) - rejects[k].push_back(object_locations[k][i]); - else - good_rects.push_back(object_locations[k][i]); - } - object_locations[k] = good_rects; - } - - return rejects; - } - - // ---------------------------------------------------------------------------------------- - - template <typename T> - struct load_to_functor - { - load_to_functor(T& obj_) : obj(obj_) {} - T& obj; - - template <typename U, typename V> - void operator()(const U& u, V& v) - { - obj.load(u,v); - } - }; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type, - typename feature_extractor, - typename box_generator - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - const structural_object_detection_trainer<scan_image_boxes<feature_extractor, box_generator> >& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - box_generator bg = trainer.get_scanner().get_box_generator(); - return impl::remove_unobtainable_rectangles(bg, trainer, images, object_locations); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type, - typename feature_extractor - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - const structural_object_detection_trainer<scan_image_custom<feature_extractor> >& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ) - { - feature_extractor fe; - fe.copy_configuration(trainer.get_scanner().get_feature_extractor()); - impl::load_to_functor<feature_extractor> bg(fe); - return impl::remove_unobtainable_rectangles(bg, trainer, images, object_locations); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_Hh_ - diff --git a/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles_abstract.h b/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles_abstract.h deleted file mode 100644 index 328326f1c..000000000 --- a/ml/dlib/dlib/image_processing/remove_unobtainable_rectangles_abstract.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_ABSTRACT_Hh_ -#ifdef DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_ABSTRACT_Hh_ - -#include "scan_image_pyramid_abstract.h" -#include "scan_image_boxes_abstract.h" -#include "scan_image_custom_abstract.h" -#include "scan_fhog_pyramid_abstract.h" -#include "../svm/structural_object_detection_trainer_abstract.h" -#include "../geometry.h" - - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type, - typename image_array_type - > - std::vector<std::vector<rectangle> > remove_unobtainable_rectangles ( - const structural_object_detection_trainer<image_scanner_type>& trainer, - const image_array_type& images, - std::vector<std::vector<rectangle> >& object_locations - ); - /*! - requires - - image_scanner_type must be either scan_image_boxes, scan_image_pyramid, - scan_image_custom, or scan_fhog_pyramid. - - images.size() == object_locations.size() - ensures - - Recall that the image scanner objects can't produce all possible rectangles - as object detections since they only consider a limited subset of all possible - object positions. Moreover, the structural_object_detection_trainer requires - its input training data to not contain any object positions which are unobtainable - by its scanner object. Therefore, remove_unobtainable_rectangles() is a tool - to filter out these unobtainable rectangles from the training data before giving - it to a structural_object_detection_trainer. - - This function interprets object_locations[i] as the set of object positions for - image[i], for all valid i. - - In particular, this function removes unobtainable rectangles from object_locations - and also returns a vector V such that: - - V.size() == object_locations.size() - - for all valid i: - - V[i] == the set of rectangles removed from object_locations[i] - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_REMOVE_UnOBTAINABLE_RECTANGLES_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/render_face_detections.h b/ml/dlib/dlib/image_processing/render_face_detections.h deleted file mode 100644 index 96ff8971f..000000000 --- a/ml/dlib/dlib/image_processing/render_face_detections.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_RENDER_FACE_DeTECTIONS_H_ -#define DLIB_RENDER_FACE_DeTECTIONS_H_ - -#include "full_object_detection.h" -#include "../gui_widgets.h" -#include "render_face_detections_abstract.h" -#include <vector> - -namespace dlib -{ - inline std::vector<image_window::overlay_line> render_face_detections ( - const std::vector<full_object_detection>& dets, - const rgb_pixel color = rgb_pixel(0,255,0) - ) - { - std::vector<image_window::overlay_line> lines; - for (unsigned long i = 0; i < dets.size(); ++i) - { - DLIB_CASSERT(dets[i].num_parts() == 68 || dets[i].num_parts() == 5, - "\t std::vector<image_window::overlay_line> render_face_detections()" - << "\n\t You have to give either a 5 point or 68 point face landmarking output to this function. " - << "\n\t dets["<<i<<"].num_parts(): " << dets[i].num_parts() - ); - - const full_object_detection& d = dets[i]; - - if (d.num_parts() == 5) - { - lines.push_back(image_window::overlay_line(d.part(0), d.part(1), color)); - lines.push_back(image_window::overlay_line(d.part(1), d.part(4), color)); - lines.push_back(image_window::overlay_line(d.part(4), d.part(3), color)); - lines.push_back(image_window::overlay_line(d.part(3), d.part(2), color)); - } - else - { - // Around Chin. Ear to Ear - for (unsigned long i = 1; i <= 16; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - - // Line on top of nose - for (unsigned long i = 28; i <= 30; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - - // left eyebrow - for (unsigned long i = 18; i <= 21; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - // Right eyebrow - for (unsigned long i = 23; i <= 26; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - // Bottom part of the nose - for (unsigned long i = 31; i <= 35; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - // Line from the nose to the bottom part above - lines.push_back(image_window::overlay_line(d.part(30), d.part(35), color)); - - // Left eye - for (unsigned long i = 37; i <= 41; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - lines.push_back(image_window::overlay_line(d.part(36), d.part(41), color)); - - // Right eye - for (unsigned long i = 43; i <= 47; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - lines.push_back(image_window::overlay_line(d.part(42), d.part(47), color)); - - // Lips outer part - for (unsigned long i = 49; i <= 59; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - lines.push_back(image_window::overlay_line(d.part(48), d.part(59), color)); - - // Lips inside part - for (unsigned long i = 61; i <= 67; ++i) - lines.push_back(image_window::overlay_line(d.part(i), d.part(i-1), color)); - lines.push_back(image_window::overlay_line(d.part(60), d.part(67), color)); - } - } - return lines; - } - -// ---------------------------------------------------------------------------------------- - - inline std::vector<image_window::overlay_line> render_face_detections ( - const full_object_detection& det, - const rgb_pixel color = rgb_pixel(0,255,0) - ) - { - std::vector<full_object_detection> dets; - dets.push_back(det); - return render_face_detections(dets, color); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_RENDER_FACE_DeTECTIONS_H_ - diff --git a/ml/dlib/dlib/image_processing/render_face_detections_abstract.h b/ml/dlib/dlib/image_processing/render_face_detections_abstract.h deleted file mode 100644 index f609c8e8c..000000000 --- a/ml/dlib/dlib/image_processing/render_face_detections_abstract.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_RENDER_FACE_DeTECTIONS_ABSTRACT_H_ -#ifdef DLIB_RENDER_FACE_DeTECTIONS_ABSTRACT_H_ - -#include "full_object_detection_abstract.h" -#include "../gui_widgets.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - inline std::vector<image_window::overlay_line> render_face_detections ( - const std::vector<full_object_detection>& dets, - const rgb_pixel color = rgb_pixel(0,255,0) - ); - /*! - requires - - for all valid i: - - dets[i].num_parts() == 68 || dets[i].num_parts() == 5 - ensures - - Interprets the given objects as face detections with parts annotated using - either the iBUG face landmark scheme or a 5 point face annotation. We then - return a set of overlay lines that will draw the objects onto the screen in a - way that properly draws the outline of the face features defined by the part - locations. - - returns a vector with dets.size() elements, each containing the lines - necessary to render a face detection from dets. - - The 5 point face annotation scheme is assumed to be: - - det part 0 == left eye corner, outside part of eye. - - det part 1 == left eye corner, inside part of eye. - - det part 2 == right eye corner, outside part of eye. - - det part 3 == right eye corner, inside part of eye. - - det part 4 == immediately under the nose, right at the top of the philtrum. - !*/ - -// ---------------------------------------------------------------------------------------- - - inline std::vector<image_window::overlay_line> render_face_detections ( - const full_object_detection& det, - const rgb_pixel color = rgb_pixel(0,255,0) - ); - /*! - requires - - det.num_parts() == 68 || det.num_parts() == 5 - ensures - - This function is identical to the above render_face_detections() routine - except that it takes just a single full_object_detection instead of a - std::vector of them. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_RENDER_FACE_DeTECTIONS_ABSTRACT_H_ - - diff --git a/ml/dlib/dlib/image_processing/scan_fhog_pyramid.h b/ml/dlib/dlib/image_processing/scan_fhog_pyramid.h deleted file mode 100644 index 5ae0310af..000000000 --- a/ml/dlib/dlib/image_processing/scan_fhog_pyramid.h +++ /dev/null @@ -1,1348 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_fHOG_PYRAMID_Hh_ -#define DLIB_SCAN_fHOG_PYRAMID_Hh_ - -#include "scan_fhog_pyramid_abstract.h" -#include "../matrix.h" -#include "../image_transforms.h" -#include "../array.h" -#include "../array2d.h" -#include "object_detector.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class default_fhog_feature_extractor - { - public: - inline rectangle image_to_feats ( - const rectangle& rect, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const - { - return image_to_fhog(rect, cell_size, filter_rows_padding, filter_cols_padding); - } - - inline rectangle feats_to_image ( - const rectangle& rect, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const - { - return fhog_to_image(rect, cell_size, filter_rows_padding, filter_cols_padding); - } - - template < - typename image_type - > - void operator()( - const image_type& img, - dlib::array<array2d<float> >& hog, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const - { - extract_fhog_features(img,hog,cell_size,filter_rows_padding,filter_cols_padding); - } - - inline unsigned long get_num_planes ( - ) const - { - return 31; - } - }; - - inline void serialize (const default_fhog_feature_extractor&, std::ostream&) {} - inline void deserialize (default_fhog_feature_extractor&, std::istream&) {} - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type = default_fhog_feature_extractor - > - class scan_fhog_pyramid : noncopyable - { - - public: - - typedef matrix<double,0,1> feature_vector_type; - - typedef Pyramid_type pyramid_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_fhog_pyramid ( - ); - - explicit scan_fhog_pyramid ( - const feature_extractor_type& fe_ - ); - - template < - typename image_type - > - void load ( - const image_type& img - ); - - inline bool is_loaded_with_image ( - ) const; - - inline void copy_configuration ( - const scan_fhog_pyramid& item - ); - - void set_detection_window_size ( - unsigned long width, - unsigned long height - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(width > 0 && height > 0, - "\t void scan_fhog_pyramid::set_detection_window_size()" - << "\n\t Invalid inputs were given to this function " - << "\n\t width: " << width - << "\n\t height: " << height - << "\n\t this: " << this - ); - - window_width = width; - window_height = height; - feats.clear(); - } - - inline unsigned long get_detection_window_width ( - ) const { return window_width; } - inline unsigned long get_detection_window_height ( - ) const { return window_height; } - - inline unsigned long get_num_detection_templates ( - ) const; - - inline unsigned long get_num_movable_components_per_detection_template ( - ) const; - - void set_padding ( - unsigned long new_padding - ) - { - padding = new_padding; - feats.clear(); - } - - unsigned long get_padding ( - ) const { return padding; } - - void set_cell_size ( - unsigned long new_cell_size - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(new_cell_size > 0 , - "\t void scan_fhog_pyramid::set_cell_size()" - << "\n\t You can't have zero sized fHOG cells. " - << "\n\t this: " << this - ); - - cell_size = new_cell_size; - feats.clear(); - } - - unsigned long get_cell_size ( - ) const { return cell_size; } - - inline long get_num_dimensions ( - ) const; - - unsigned long get_max_pyramid_levels ( - ) const; - - const feature_extractor_type& get_feature_extractor( - ) const { return fe; } - - void set_max_pyramid_levels ( - unsigned long max_levels - ); - - void set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ); - - inline unsigned long get_min_pyramid_layer_width ( - ) const; - - inline unsigned long get_min_pyramid_layer_height ( - ) const; - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - w.size() >= get_num_dimensions(), - "\t void scan_fhog_pyramid::detect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t w.size(): " << w.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - fhog_filterbank temp = build_fhog_filterbank(w); - detect(temp, dets, thresh); - } - - class fhog_filterbank - { - friend class scan_fhog_pyramid; - public: - inline long get_num_dimensions() const - { - unsigned long dims = 0; - for (unsigned long i = 0; i < filters.size(); ++i) - { - dims += filters[i].size(); - } - return dims; - } - - const std::vector<matrix<float> >& get_filters() const { return filters;} - - unsigned long num_separable_filters() const - { - unsigned long num = 0; - for (unsigned long i = 0; i < row_filters.size(); ++i) - { - num += row_filters[i].size(); - } - return num; - } - - std::vector<matrix<float> > filters; - std::vector<std::vector<matrix<float,0,1> > > row_filters, col_filters; - }; - - fhog_filterbank build_fhog_filterbank ( - const feature_vector_type& weights - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(weights.size() >= get_num_dimensions(), - "\t fhog_filterbank scan_fhog_pyramid::build_fhog_filterbank()" - << "\n\t The number of weights isn't enough to fill out the filterbank. " - << "\n\t weights.size(): " << weights.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - fhog_filterbank temp; - temp.filters.resize(fe.get_num_planes()); - temp.row_filters.resize(fe.get_num_planes()); - temp.col_filters.resize(fe.get_num_planes()); - - // load filters from w - unsigned long width, height; - compute_fhog_window_size(width, height); - const long size = width*height; - for (unsigned long i = 0; i < temp.filters.size(); ++i) - { - matrix<double> u,v,w,f; - f = reshape(rowm(weights, range(i*size, (i+1)*size-1)), height, width); - temp.filters[i] = matrix_cast<float>(f); - - svd3(f, u,w,v); - - matrix<double> w2 = w; - rsort_columns(u,w); - rsort_columns(v,w2); - - double thresh = std::max(1e-4, max(w)*0.001); - w = round_zeros(w, thresh); - - - for (long j = 0; j < w.size(); ++j) - { - if (w(j) != 0) - { - temp.col_filters[i].push_back(matrix_cast<float>(colm(u,j)*std::sqrt(w(j)))); - temp.row_filters[i].push_back(matrix_cast<float>(colm(v,j)*std::sqrt(w(j)))); - } - } - } - - return temp; - } - - void detect ( - const fhog_filterbank& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - - double get_nuclear_norm_regularization_strength ( - ) const { return nuclear_norm_regularization_strength; } - - void set_nuclear_norm_regularization_strength ( - double strength - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(strength >= 0 , - "\t void scan_fhog_pyramid::set_nuclear_norm_regularization_strength()" - << "\n\t You can't have a negative regularization strength." - << "\n\t strength: " << strength - << "\n\t this: " << this - ); - - nuclear_norm_regularization_strength = strength; - } - - unsigned long get_fhog_window_width ( - ) const - { - unsigned long width, height; - compute_fhog_window_size(width, height); - return width; - } - - unsigned long get_fhog_window_height ( - ) const - { - unsigned long width, height; - compute_fhog_window_size(width, height); - return height; - } - - template <typename T, typename U> - friend void serialize ( - const scan_fhog_pyramid<T,U>& item, - std::ostream& out - ); - - template <typename T, typename U> - friend void deserialize ( - scan_fhog_pyramid<T,U>& item, - std::istream& in - ); - - private: - inline void compute_fhog_window_size( - unsigned long& width, - unsigned long& height - ) const - { - const rectangle rect = centered_rect(point(0,0),window_width,window_height); - const rectangle temp = grow_rect(fe.image_to_feats(rect, cell_size, 1, 1), padding); - width = temp.width(); - height = temp.height(); - } - - void get_mapped_rect_and_metadata ( - const unsigned long number_pyramid_levels, - const rectangle& rect, - rectangle& mapped_rect, - rectangle& fhog_rect, - unsigned long& best_level - ) const; - - double get_match_score ( - rectangle r1, - rectangle r2 - ) const - { - // make the rectangles overlap as much as possible before computing the match score. - r1 = move_rect(r1, r2.tl_corner()); - return (r1.intersect(r2).area())/(double)(r1 + r2).area(); - } - - typedef array<array2d<float> > fhog_image; - - feature_extractor_type fe; - array<fhog_image> feats; - int cell_size; - unsigned long padding; - unsigned long window_width; - unsigned long window_height; - unsigned long max_pyramid_levels; - unsigned long min_pyramid_layer_width; - unsigned long min_pyramid_layer_height; - double nuclear_norm_regularization_strength; - - void init() - { - cell_size = 8; - padding = 1; - window_width = 64; - window_height = 64; - max_pyramid_levels = 1000; - min_pyramid_layer_width = 64; - min_pyramid_layer_height = 64; - nuclear_norm_regularization_strength = 0; - } - - }; - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - template <typename fhog_filterbank> - rectangle apply_filters_to_fhog ( - const fhog_filterbank& w, - const array<array2d<float> >& feats, - array2d<float>& saliency_image - ) - { - const unsigned long num_separable_filters = w.num_separable_filters(); - rectangle area; - // use the separable filters if they would be faster than running the regular filters. - if (num_separable_filters > w.filters.size()*std::min(w.filters[0].nr(),w.filters[0].nc())/3.0) - { - area = spatially_filter_image(feats[0], saliency_image, w.filters[0]); - for (unsigned long i = 1; i < w.filters.size(); ++i) - { - // now we filter but the output adds to saliency_image rather than - // overwriting it. - spatially_filter_image(feats[i], saliency_image, w.filters[i], 1, false, true); - } - } - else - { - saliency_image.clear(); - array2d<float> scratch; - - // find the first filter to apply - unsigned long i = 0; - while (i < w.row_filters.size() && w.row_filters[i].size() == 0) - ++i; - - for (; i < w.row_filters.size(); ++i) - { - for (unsigned long j = 0; j < w.row_filters[i].size(); ++j) - { - if (saliency_image.size() == 0) - area = float_spatially_filter_image_separable(feats[i], saliency_image, w.row_filters[i][j], w.col_filters[i][j],scratch,false); - else - area = float_spatially_filter_image_separable(feats[i], saliency_image, w.row_filters[i][j], w.col_filters[i][j],scratch,true); - } - } - if (saliency_image.size() == 0) - { - saliency_image.set_size(feats[0].nr(), feats[0].nc()); - assign_all_pixels(saliency_image, 0); - } - } - return area; - } - } - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void serialize ( - const scan_fhog_pyramid<T,U>& item, - std::ostream& out - ) - { - int version = 1; - serialize(version, out); - serialize(item.fe, out); - serialize(item.feats, out); - serialize(item.cell_size, out); - serialize(item.padding, out); - serialize(item.window_width, out); - serialize(item.window_height, out); - serialize(item.max_pyramid_levels, out); - serialize(item.min_pyramid_layer_width, out); - serialize(item.min_pyramid_layer_height, out); - serialize(item.nuclear_norm_regularization_strength, out); - serialize(item.get_num_dimensions(), out); - } - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void deserialize ( - scan_fhog_pyramid<T,U>& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version != 1) - throw serialization_error("Unsupported version found when deserializing a scan_fhog_pyramid object."); - - deserialize(item.fe, in); - deserialize(item.feats, in); - deserialize(item.cell_size, in); - deserialize(item.padding, in); - deserialize(item.window_width, in); - deserialize(item.window_height, in); - deserialize(item.max_pyramid_levels, in); - deserialize(item.min_pyramid_layer_width, in); - deserialize(item.min_pyramid_layer_height, in); - deserialize(item.nuclear_norm_regularization_strength, in); - - // When developing some feature extractor, it's easy to accidentally change its - // number of dimensions and then try to deserialize data from an older version of - // your extractor into the current code. This check is here to catch that kind of - // user error. - long dims; - deserialize(dims, in); - if (item.get_num_dimensions() != dims) - throw serialization_error("Number of dimensions in serialized scan_fhog_pyramid doesn't match the expected number."); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// scan_fhog_pyramid member functions -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - scan_fhog_pyramid ( - ) - { - init(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - scan_fhog_pyramid ( - const feature_extractor_type& fe_ - ) - { - init(); - fe = fe_; - } - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - template < - typename pyramid_type, - typename image_type, - typename feature_extractor_type - > - void create_fhog_pyramid ( - const image_type& img, - const feature_extractor_type& fe, - array<array<array2d<float> > >& feats, - int cell_size, - int filter_rows_padding, - int filter_cols_padding, - unsigned long min_pyramid_layer_width, - unsigned long min_pyramid_layer_height, - unsigned long max_pyramid_levels - ) - { - unsigned long levels = 0; - rectangle rect = get_rect(img); - - // figure out how many pyramid levels we should be using based on the image size - pyramid_type pyr; - do - { - rect = pyr.rect_down(rect); - ++levels; - } while (rect.width() >= min_pyramid_layer_width && rect.height() >= min_pyramid_layer_height && - levels < max_pyramid_levels); - - if (feats.max_size() < levels) - feats.set_max_size(levels); - feats.set_size(levels); - - - - // build our feature pyramid - fe(img, feats[0], cell_size,filter_rows_padding,filter_cols_padding); - DLIB_ASSERT(feats[0].size() == fe.get_num_planes(), - "Invalid feature extractor used with dlib::scan_fhog_pyramid. The output does not have the \n" - "indicated number of planes."); - - if (feats.size() > 1) - { - typedef typename image_traits<image_type>::pixel_type pixel_type; - array2d<pixel_type> temp1, temp2; - pyr(img, temp1); - fe(temp1, feats[1], cell_size,filter_rows_padding,filter_cols_padding); - swap(temp1,temp2); - - for (unsigned long i = 2; i < feats.size(); ++i) - { - pyr(temp2, temp1); - fe(temp1, feats[i], cell_size,filter_rows_padding,filter_cols_padding); - swap(temp1,temp2); - } - } - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - template < - typename image_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - load ( - const image_type& img - ) - { - unsigned long width, height; - compute_fhog_window_size(width,height); - impl::create_fhog_pyramid<Pyramid_type>(img, fe, feats, cell_size, height, - width, min_pyramid_layer_width, min_pyramid_layer_height, - max_pyramid_levels); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - bool scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - is_loaded_with_image ( - ) const - { - return feats.size() != 0; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - copy_configuration ( - const scan_fhog_pyramid& item - ) - { - cell_size = item.cell_size; - padding = item.padding; - window_width = item.window_width; - window_height = item.window_height; - max_pyramid_levels = item.max_pyramid_levels; - min_pyramid_layer_width = item.min_pyramid_layer_width; - min_pyramid_layer_height = item.min_pyramid_layer_height; - nuclear_norm_regularization_strength = item.nuclear_norm_regularization_strength; - fe = item.fe; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_num_detection_templates ( - ) const - { - return 1; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_num_movable_components_per_detection_template ( - ) const - { - return 0; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_num_dimensions ( - ) const - { - unsigned long width, height; - compute_fhog_window_size(width,height); - return width*height*fe.get_num_planes(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_max_pyramid_levels ( - ) const - { - return max_pyramid_levels; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - set_max_pyramid_levels ( - unsigned long max_levels - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(max_levels > 0 , - "\t void scan_fhog_pyramid::set_max_pyramid_levels()" - << "\n\t You can't have zero levels. " - << "\n\t max_levels: " << max_levels - << "\n\t this: " << this - ); - - max_pyramid_levels = max_levels; - } - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - inline bool compare_pair_rect ( - const std::pair<double, rectangle>& a, - const std::pair<double, rectangle>& b - ) - { - return a.first < b.first; - } - - template < - typename pyramid_type, - typename feature_extractor_type, - typename fhog_filterbank - > - void detect_from_fhog_pyramid ( - const array<array<array2d<float> > >& feats, - const feature_extractor_type& fe, - const fhog_filterbank& w, - const double thresh, - const unsigned long det_box_height, - const unsigned long det_box_width, - const int cell_size, - const int filter_rows_padding, - const int filter_cols_padding, - std::vector<std::pair<double, rectangle> >& dets - ) - { - dets.clear(); - - array2d<float> saliency_image; - pyramid_type pyr; - - // for all pyramid levels - for (unsigned long l = 0; l < feats.size(); ++l) - { - const rectangle area = apply_filters_to_fhog(w, feats[l], saliency_image); - - // now search the saliency image for any detections - for (long r = area.top(); r <= area.bottom(); ++r) - { - for (long c = area.left(); c <= area.right(); ++c) - { - // if we found a detection - if (saliency_image[r][c] >= thresh) - { - rectangle rect = fe.feats_to_image(centered_rect(point(c,r),det_box_width,det_box_height), - cell_size, filter_rows_padding, filter_cols_padding); - rect = pyr.rect_up(rect, l); - dets.push_back(std::make_pair(saliency_image[r][c], rect)); - } - } - } - } - - std::sort(dets.rbegin(), dets.rend(), compare_pair_rect); - } - - inline bool overlaps_any_box ( - const test_box_overlap& tester, - const std::vector<rect_detection>& rects, - const rect_detection& rect - ) - { - for (unsigned long i = 0; i < rects.size(); ++i) - { - // Only compare detections from the same detector. That is, we don't want - // the output of one detector to stop on the output of another detector. - if (rects[i].weight_index == rect.weight_index && tester(rects[i].rect, rect.rect)) - return true; - } - return false; - } - - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - detect ( - const fhog_filterbank& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - w.get_num_dimensions() == get_num_dimensions(), - "\t void scan_fhog_pyramid::detect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t w.get_num_dimensions(): " << w.get_num_dimensions() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - unsigned long width, height; - compute_fhog_window_size(width,height); - - impl::detect_from_fhog_pyramid<pyramid_type>(feats, fe, w, thresh, - height-2*padding, width-2*padding, cell_size, height, width, dets); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - const rectangle scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_best_matching_rect ( - const rectangle& rect - ) const - { - rectangle mapped_rect, fhog_rect; - unsigned long best_level; - get_mapped_rect_and_metadata(max_pyramid_levels, rect, mapped_rect, fhog_rect, best_level); - return mapped_rect; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_mapped_rect_and_metadata ( - const unsigned long number_pyramid_levels, - const rectangle& rect, - rectangle& mapped_rect, - rectangle& fhog_rect, - unsigned long& best_level - ) const - { - pyramid_type pyr; - best_level = 0; - double best_match_score = -1; - - - unsigned long width, height; - compute_fhog_window_size(width,height); - - // Figure out the pyramid level which best matches rect against our detection - // window. - for (unsigned long l = 0; l < number_pyramid_levels; ++l) - { - const rectangle rect_fhog_space = fe.image_to_feats(pyr.rect_down(rect,l), cell_size, height,width); - - const rectangle win_image_space = pyr.rect_up(fe.feats_to_image(centered_rect(center(rect_fhog_space),width-2*padding,height-2*padding), cell_size, height,width), l); - - const double match_score = get_match_score(win_image_space, rect); - if (match_score > best_match_score) - { - best_match_score = match_score; - best_level = l; - fhog_rect = centered_rect(center(rect_fhog_space), width, height); - } - - if (rect_fhog_space.area() <= 1) - break; - } - mapped_rect = pyr.rect_up(fe.feats_to_image(shrink_rect(fhog_rect,padding), cell_size,height,width),best_level); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - full_object_detection scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& - ) const - { - return full_object_detection(rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - psi.size() >= get_num_dimensions() && - obj.num_parts() == 0, - "\t void scan_fhog_pyramid::get_feature_vector()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t psi.size(): " << psi.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t obj.num_parts(): " << obj.num_parts() - << "\n\t this: " << this - ); - - - - rectangle mapped_rect; - unsigned long best_level; - rectangle fhog_rect; - get_mapped_rect_and_metadata(feats.size(), obj.get_rect(), mapped_rect, fhog_rect, best_level); - - - long i = 0; - for (unsigned long ii = 0; ii < feats[best_level].size(); ++ii) - { - const rectangle rect = get_rect(feats[best_level][0]); - for (long r = fhog_rect.top(); r <= fhog_rect.bottom(); ++r) - { - for (long c = fhog_rect.left(); c <= fhog_rect.right(); ++c) - { - if (rect.contains(c,r)) - psi(i) += feats[best_level][ii][r][c]; - ++i; - } - } - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - void scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(width > 0 && height > 0 , - "\t void scan_fhog_pyramid::set_min_pyramid_layer_size()" - << "\n\t These sizes can't be zero. " - << "\n\t width: " << width - << "\n\t height: " << height - << "\n\t this: " << this - ); - - min_pyramid_layer_width = width; - min_pyramid_layer_height = height; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_min_pyramid_layer_width ( - ) const - { - return min_pyramid_layer_width; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long scan_fhog_pyramid<Pyramid_type,feature_extractor_type>:: - get_min_pyramid_layer_height ( - ) const - { - return min_pyramid_layer_height; - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - matrix<unsigned char> draw_fhog ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - const unsigned long weight_index = 0, - const long cell_draw_size = 15 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(weight_index < detector.num_detectors(), - "\t matrix draw_fhog()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t weight_index: " << weight_index - << "\n\t detector.num_detectors(): " << detector.num_detectors() - ); - DLIB_ASSERT(cell_draw_size > 0 && detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions(), - "\t matrix draw_fhog()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t cell_draw_size: " << cell_draw_size - << "\n\t weight_index: " << weight_index - << "\n\t detector.get_w(weight_index).size(): " << detector.get_w(weight_index).size() - << "\n\t detector.get_scanner().get_num_dimensions(): " << detector.get_scanner().get_num_dimensions() - ); - - typename scan_fhog_pyramid<Pyramid_type,feature_extractor_type>::fhog_filterbank fb = detector.get_scanner().build_fhog_filterbank(detector.get_w(weight_index)); - return draw_fhog(fb.get_filters(),cell_draw_size); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long num_separable_filters ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - const unsigned long weight_index = 0 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(weight_index < detector.num_detectors(), - "\t unsigned long num_separable_filters()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t weight_index: " << weight_index - << "\n\t detector.num_detectors(): " << detector.num_detectors() - ); - DLIB_ASSERT(detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions() , - "\t unsigned long num_separable_filters()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t detector.get_w(weight_index).size(): " << detector.get_w(weight_index).size() - << "\n\t detector.get_scanner().get_num_dimensions(): " << detector.get_scanner().get_num_dimensions() - ); - - typename scan_fhog_pyramid<Pyramid_type,feature_extractor_type>::fhog_filterbank fb = detector.get_scanner().build_fhog_filterbank(detector.get_w(weight_index)); - return fb.num_separable_filters(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> > threshold_filter_singular_values ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - double thresh, - const unsigned long weight_index = 0 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(thresh >= 0 , - "\t object_detector threshold_filter_singular_values()" - << "\n\t Invalid inputs were given to this function." - << "\n\t thresh: " << thresh - ); - - DLIB_ASSERT(weight_index < detector.num_detectors(), - "\t object_detector threshold_filter_singular_values()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t weight_index: " << weight_index - << "\n\t detector.num_detectors(): " << detector.num_detectors() - ); - DLIB_ASSERT(detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions() , - "\t object_detector threshold_filter_singular_values()" - << "\n\t Invalid arguments were given to this function. " - << "\n\t detector.get_w(weight_index).size(): " << detector.get_w(weight_index).size() - << "\n\t detector.get_scanner().get_num_dimensions(): " << detector.get_scanner().get_num_dimensions() - ); - - - const unsigned long width = detector.get_scanner().get_fhog_window_width(); - const unsigned long height = detector.get_scanner().get_fhog_window_height(); - const long num_planes = detector.get_scanner().get_feature_extractor().get_num_planes(); - const long size = width*height; - - std::vector<matrix<double,0,1> > detector_weights; - for (unsigned long j = 0; j < detector.num_detectors(); ++j) - { - matrix<double,0,1> weights = detector.get_w(j); - - if (j == weight_index) - { - matrix<double> u,v,w,f; - for (long i = 0; i < num_planes; ++i) - { - f = reshape(rowm(weights, range(i*size, (i+1)*size-1)), height, width); - - svd3(f, u,w,v); - const double scaled_thresh = std::max(1e-3, max(w)*thresh); - w = round_zeros(w, scaled_thresh); - f = u*diagm(w)*trans(v); - - set_rowm(weights,range(i*size, (i+1)*size-1)) = reshape_to_column_vector(f); - } - } - detector_weights.push_back(weights); - } - - return object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >(detector.get_scanner(), - detector.get_overlap_tester(), - detector_weights); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type, - typename svm_struct_prob_type - > - void configure_nuclear_norm_regularizer ( - const scan_fhog_pyramid<Pyramid_type,feature_extractor_type>& scanner, - svm_struct_prob_type& prob - ) - { - const double strength = scanner.get_nuclear_norm_regularization_strength(); - const long num_planes = scanner.get_feature_extractor().get_num_planes(); - if (strength != 0) - { - const unsigned long width = scanner.get_fhog_window_width(); - const unsigned long height = scanner.get_fhog_window_height(); - for (long i = 0; i < num_planes; ++i) - { - prob.add_nuclear_norm_regularizer(i*width*height, height, width, strength); - } - prob.set_cache_based_epsilon(0.001); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - struct processed_weight_vector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> > - { - processed_weight_vector(){} - - typedef matrix<double,0,1> feature_vector_type; - typedef typename scan_fhog_pyramid<Pyramid_type,feature_extractor_type>::fhog_filterbank fhog_filterbank; - - void init ( - const scan_fhog_pyramid<Pyramid_type,feature_extractor_type>& scanner - ) - { - fb = scanner.build_fhog_filterbank(w); - } - - const fhog_filterbank& get_detect_argument() const { return fb; } - - feature_vector_type w; - fhog_filterbank fb; - - }; - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename pyramid_type, - typename image_type - > - void evaluate_detectors ( - const std::vector<object_detector<scan_fhog_pyramid<pyramid_type> > >& detectors, - const image_type& img, - std::vector<rect_detection>& dets, - const double adjust_threshold = 0 - ) - { - typedef scan_fhog_pyramid<pyramid_type> scanner_type; - - dets.clear(); - if (detectors.size() == 0) - return; - - const unsigned long cell_size = detectors[0].get_scanner().get_cell_size(); - - // Find the maximum sized filters and also most extreme pyramiding settings used. - unsigned long max_filter_width = 0; - unsigned long max_filter_height = 0; - unsigned long min_pyramid_layer_width = std::numeric_limits<unsigned long>::max(); - unsigned long min_pyramid_layer_height = std::numeric_limits<unsigned long>::max(); - unsigned long max_pyramid_levels = 0; - bool all_cell_sizes_the_same = true; - for (unsigned long i = 0; i < detectors.size(); ++i) - { - const scanner_type& scanner = detectors[i].get_scanner(); - max_filter_width = std::max(max_filter_width, scanner.get_fhog_window_width()); - max_filter_height = std::max(max_filter_height, scanner.get_fhog_window_height()); - max_pyramid_levels = std::max(max_pyramid_levels, scanner.get_max_pyramid_levels()); - min_pyramid_layer_width = std::min(min_pyramid_layer_width, scanner.get_min_pyramid_layer_width()); - min_pyramid_layer_height = std::min(min_pyramid_layer_height, scanner.get_min_pyramid_layer_height()); - if (cell_size != scanner.get_cell_size()) - all_cell_sizes_the_same = false; - } - - std::vector<rect_detection> dets_accum; - // Do to the HOG feature extraction to make the fhog pyramid. Again, note that we - // are making a pyramid that will work with any of the detectors. But only if all - // the cell sizes are the same. If they aren't then we have to calculate the - // pyramid for each detector individually. - array<array<array2d<float> > > feats; - if (all_cell_sizes_the_same) - { - impl::create_fhog_pyramid<pyramid_type>(img, - detectors[0].get_scanner().get_feature_extractor(), feats, cell_size, - max_filter_height, max_filter_width, min_pyramid_layer_width, - min_pyramid_layer_height, max_pyramid_levels); - } - - std::vector<std::pair<double, rectangle> > temp_dets; - for (unsigned long i = 0; i < detectors.size(); ++i) - { - const scanner_type& scanner = detectors[i].get_scanner(); - if (!all_cell_sizes_the_same) - { - impl::create_fhog_pyramid<pyramid_type>(img, - scanner.get_feature_extractor(), feats, scanner.get_cell_size(), - max_filter_height, max_filter_width, min_pyramid_layer_width, - min_pyramid_layer_height, max_pyramid_levels); - } - - const unsigned long det_box_width = scanner.get_fhog_window_width() - 2*scanner.get_padding(); - const unsigned long det_box_height = scanner.get_fhog_window_height() - 2*scanner.get_padding(); - // A single detector object might itself have multiple weight vectors in it. So - // we need to evaluate all of them. - for (unsigned d = 0; d < detectors[i].num_detectors(); ++d) - { - const double thresh = detectors[i].get_processed_w(d).w(scanner.get_num_dimensions()); - - impl::detect_from_fhog_pyramid<pyramid_type>(feats, scanner.get_feature_extractor(), - detectors[i].get_processed_w(d).get_detect_argument(), thresh+adjust_threshold, - det_box_height, det_box_width, cell_size, max_filter_height, - max_filter_width, temp_dets); - - for (unsigned long j = 0; j < temp_dets.size(); ++j) - { - rect_detection temp; - temp.detection_confidence = temp_dets[j].first-thresh; - temp.weight_index = i; - temp.rect = temp_dets[j].second; - dets_accum.push_back(temp); - } - } - } - - - // Do non-max suppression - if (detectors.size() > 1) - std::sort(dets_accum.rbegin(), dets_accum.rend()); - for (unsigned long i = 0; i < dets_accum.size(); ++i) - { - const test_box_overlap tester = detectors[dets_accum[i].weight_index].get_overlap_tester(); - if (impl::overlaps_any_box(tester, dets, dets_accum[i])) - continue; - - dets.push_back(dets_accum[i]); - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename image_type - > - std::vector<rectangle> evaluate_detectors ( - const std::vector<object_detector<scan_fhog_pyramid<Pyramid_type> > >& detectors, - const image_type& img, - const double adjust_threshold = 0 - ) - { - std::vector<rectangle> out_dets; - std::vector<rect_detection> dets; - evaluate_detectors(detectors, img, dets, adjust_threshold); - out_dets.reserve(dets.size()); - for (unsigned long i = 0; i < dets.size(); ++i) - out_dets.push_back(dets[i].rect); - return out_dets; - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_fHOG_PYRAMID_Hh_ - diff --git a/ml/dlib/dlib/image_processing/scan_fhog_pyramid_abstract.h b/ml/dlib/dlib/image_processing/scan_fhog_pyramid_abstract.h deleted file mode 100644 index d12a2b2b8..000000000 --- a/ml/dlib/dlib/image_processing/scan_fhog_pyramid_abstract.h +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_fHOG_PYRAMID_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_fHOG_PYRAMID_ABSTRACT_Hh_ - -#include <vector> -#include "../image_transforms/fhog_abstract.h" -#include "object_detector_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - matrix<unsigned char> draw_fhog ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - const unsigned long weight_index = 0, - const long cell_draw_size = 15 - ); - /*! - requires - - cell_draw_size > 0 - - weight_index < detector.num_detectors() - - detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions() - (i.e. the detector must have been populated with a HOG filter) - ensures - - Converts the HOG filters in the given detector (specifically, the filters in - detector.get_w(weight_index)) into an image suitable for display on the - screen. In particular, we draw all the HOG cells into a grayscale image in a - way that shows the magnitude and orientation of the gradient energy in each - cell. The resulting image is then returned. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - unsigned long num_separable_filters ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - const unsigned long weight_index = 0 - ); - /*! - requires - - weight_index < detector.num_detectors() - - detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions() - (i.e. the detector must have been populated with a HOG filter) - ensures - - Returns the number of separable filters necessary to represent the HOG - filters in the given detector's weight_index'th filter. This is the filter - defined by detector.get_w(weight_index). - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename feature_extractor_type - > - object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> > threshold_filter_singular_values ( - const object_detector<scan_fhog_pyramid<Pyramid_type,feature_extractor_type> >& detector, - double thresh, - const unsigned long weight_index = 0 - ); - /*! - requires - - thresh >= 0 - - weight_index < detector.num_detectors() - - detector.get_w(weight_index).size() >= detector.get_scanner().get_num_dimensions() - (i.e. the detector must have been populated with a HOG filter) - ensures - - Removes all components of the filters in the given detector that have - singular values that are smaller than the given threshold. Therefore, this - function allows you to control how many separable filters are in a detector. - In particular, as thresh gets larger the quantity - num_separable_filters(threshold_filter_singular_values(detector,thresh,weight_index),weight_index) - will generally get smaller and therefore give a faster running detector. - However, note that at some point a large enough thresh will drop too much - information from the filters and their accuracy will suffer. - - returns the updated detector - !*/ - -// ---------------------------------------------------------------------------------------- - - class default_fhog_feature_extractor - { - /*! - WHAT THIS OBJECT REPRESENTS - The scan_fhog_pyramid object defined below is primarily meant to be used - with the feature extraction technique implemented by extract_fhog_features(). - This technique can generally be understood as taking an input image and - outputting a multi-planed output image of floating point numbers that - somehow describe the image contents. Since there are many ways to define - how this feature mapping is performed, the scan_fhog_pyramid allows you to - replace the extract_fhog_features() method with a customized method of your - choosing. To do this you implement a class with the same interface as - default_fhog_feature_extractor. - - Therefore, the point of default_fhog_feature_extractor is two fold. First, - it provides the default FHOG feature extraction method used by scan_fhog_pyramid. - Second, it serves to document the interface you need to implement to define - your own custom HOG style feature extraction. - !*/ - - public: - - rectangle image_to_feats ( - const rectangle& rect, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const { return image_to_fhog(rect, cell_size, filter_rows_padding, filter_cols_padding); } - /*! - requires - - cell_size > 0 - - filter_rows_padding > 0 - - filter_cols_padding > 0 - ensures - - Maps a rectangle from the coordinates in an input image to the corresponding - area in the output feature image. - !*/ - - rectangle feats_to_image ( - const rectangle& rect, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const { return fhog_to_image(rect, cell_size, filter_rows_padding, filter_cols_padding); } - /*! - requires - - cell_size > 0 - - filter_rows_padding > 0 - - filter_cols_padding > 0 - ensures - - Maps a rectangle from the coordinates of the hog feature image back to - the input image. - - Mapping from feature space to image space is an invertible - transformation. That is, for any rectangle R we have: - R == image_to_feats(feats_to_image(R,cell_size,filter_rows_padding,filter_cols_padding), - cell_size,filter_rows_padding,filter_cols_padding). - !*/ - - template < - typename image_type - > - void operator()( - const image_type& img, - dlib::array<array2d<float> >& hog, - int cell_size, - int filter_rows_padding, - int filter_cols_padding - ) const { extract_fhog_features(img,hog,cell_size,filter_rows_padding,filter_cols_padding); } - /*! - requires - - image_type == is an implementation of array2d/array2d_kernel_abstract.h - - img contains some kind of pixel type. - (i.e. pixel_traits<typename image_type::type> is defined) - ensures - - Extracts FHOG features by calling extract_fhog_features(). The results are - stored into #hog. Note that if you are implementing your own feature extractor you can - pretty much do whatever you want in terms of feature extraction so long as the following - conditions are met: - - #hog.size() == get_num_planes() - - Each image plane in #hog has the same dimensions. - - for all valid i, r, and c: - - #hog[i][r][c] == a feature value describing the image content centered at the - following pixel location in img: - feats_to_image(point(c,r),cell_size,filter_rows_padding,filter_cols_padding) - !*/ - - inline unsigned long get_num_planes ( - ) const { return 31; } - /*! - ensures - - returns the number of planes in the hog image output by the operator() - method. - !*/ - }; - - inline void serialize (const default_fhog_feature_extractor&, std::ostream&) {} - inline void deserialize (default_fhog_feature_extractor&, std::istream&) {} - /*! - Provides serialization support. Note that there is no state in the default hog - feature extractor so these functions do nothing. But if you define a custom - feature extractor then make sure you remember to serialize any state in your - feature extractor. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type = default_fhog_feature_extractor - > - class scan_fhog_pyramid : noncopyable - { - /*! - REQUIREMENTS ON Pyramid_type - - Must be one of the pyramid_down objects defined in - dlib/image_transforms/image_pyramid_abstract.h or an object with a - compatible interface - - REQUIREMENTS ON Feature_extractor_type - - Must be a type with an interface compatible with the - default_fhog_feature_extractor. - - INITIAL VALUE - - get_padding() == 1 - - get_cell_size() == 8 - - get_detection_window_width() == 64 - - get_detection_window_height() == 64 - - get_max_pyramid_levels() == 1000 - - get_min_pyramid_layer_width() == 64 - - get_min_pyramid_layer_height() == 64 - - get_nuclear_norm_regularization_strength() == 0 - - WHAT THIS OBJECT REPRESENTS - This object is a tool for running a fixed sized sliding window classifier - over an image pyramid. In particular, it slides a linear classifier over - a HOG pyramid as discussed in the paper: - Histograms of Oriented Gradients for Human Detection by Navneet Dalal - and Bill Triggs, CVPR 2005 - However, we augment the method slightly to use the version of HOG features - from: - Object Detection with Discriminatively Trained Part Based Models by - P. Felzenszwalb, R. Girshick, D. McAllester, D. Ramanan - IEEE Transactions on Pattern Analysis and Machine Intelligence, Vol. 32, No. 9, Sep. 2010 - Since these HOG features have been shown to give superior performance. - - THREAD SAFETY - Concurrent access to an instance of this object is not safe and should be - protected by a mutex lock except for the case where you are copying the - configuration (via copy_configuration()) of a scan_fhog_pyramid object to - many other threads. In this case, it is safe to copy the configuration of - a shared object so long as no other operations are performed on it. - !*/ - - public: - typedef matrix<double,0,1> feature_vector_type; - typedef Pyramid_type pyramid_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_fhog_pyramid ( - ); - /*! - ensures - - this object is properly initialized - !*/ - - explicit scan_fhog_pyramid ( - const feature_extractor_type& fe - ); - /*! - ensures - - this object is properly initialized - - #get_feature_extractor() == fe - !*/ - - template < - typename image_type - > - void load ( - const image_type& img - ); - /*! - requires - - image_type == is an implementation of array2d/array2d_kernel_abstract.h - - img contains some kind of pixel type. - (i.e. pixel_traits<typename image_type::type> is defined) - ensures - - #is_loaded_with_image() == true - - This object is ready to run a classifier over img to detect object - locations. Call detect() to do this. - !*/ - - const feature_extractor_type& get_feature_extractor( - ) const; - /*! - ensures - - returns a const reference to the feature extractor used by this object. - !*/ - - bool is_loaded_with_image ( - ) const; - /*! - ensures - - returns true if this object has been loaded with an image to process and - false otherwise. - !*/ - - void copy_configuration ( - const scan_fhog_pyramid& item - ); - /*! - ensures - - Copies all the state information of item into *this, except for state - information populated by load(). More precisely, given two scan_fhog_pyramid - objects S1 and S2, the following sequence of instructions should always - result in both of them having the exact same state: - S2.copy_configuration(S1); - S1.load(img); - S2.load(img); - !*/ - - void set_detection_window_size ( - unsigned long window_width, - unsigned long window_height - ); - /*! - requires - - window_width > 0 - - window_height > 0 - ensures - - When detect() is called, this object scans a window that is of the given - width and height (in pixels) over each layer in an image pyramid. This - means that the rectangle detections which come out of detect() will have - a width to height ratio approximately equal to window_width/window_height - and will be approximately window_width*window_height pixels in area or - larger. Therefore, the smallest object that can be detected is roughly - window_width by window_height pixels in size. - - #get_detection_window_width() == window_width - - #get_detection_window_height() == window_height - - Since we use a HOG feature representation, the detection procedure works - as follows: - Step 1. Make an image pyramid. - Step 2. Convert each layer of the image pyramid into a multi-planed HOG "image". - (the number of bands is given by get_feature_extractor().get_num_planes()) - Step 3. Scan a linear classifier over each HOG image in the pyramid. - Moreover, the HOG features quantize the input image into a grid of cells, - each cell being get_cell_size() by get_cell_size() pixels in size. So - when we scan the object detector over the pyramid we are scanning an - appropriately sized window over these smaller quantized HOG features. In - particular, the size of the window we scan over the HOG feature pyramid - is #get_fhog_window_width() by #get_fhog_window_height() HOG cells in - size. - - #is_loaded_with_image() == false - !*/ - - unsigned long get_detection_window_width ( - ) const; - /*! - ensures - - returns the width, in pixels, of the detection window that is scanned - over the image when detect() is called. - !*/ - - inline unsigned long get_detection_window_height ( - ) const; - /*! - ensures - - returns the height, in pixels, of the detection window that is scanned - over the image when detect() is called. - !*/ - - unsigned long get_fhog_window_width ( - ) const; - /*! - ensures - - Returns the width of the HOG scanning window in terms of HOG cell blocks. - Note that this is a function of get_detection_window_width(), get_cell_size(), - and get_padding() and is therefore not something you set directly. - - #get_fhog_window_width() is approximately equal to the number of HOG cells - that fit into get_detection_window_width() pixels plus 2*get_padding() - since we include additional padding around each window to add context. - !*/ - - unsigned long get_fhog_window_height ( - ) const; - /*! - ensures - - Returns the height of the HOG scanning window in terms of HOG cell blocks. - Note that this is a function of get_detection_window_height(), get_cell_size(), - and get_padding() and is therefore not something you set directly. - - #get_fhog_window_height() is approximately equal to the number of HOG cells - that fit into get_detection_window_height() pixels plus 2*get_padding() - since we include additional padding around each window to add context. - !*/ - - void set_padding ( - unsigned long new_padding - ); - /*! - ensures - - #get_padding() == new_padding - - #is_loaded_with_image() == false - !*/ - - unsigned long get_padding ( - ) const; - /*! - ensures - - The HOG windows scanned over the HOG pyramid can include additional HOG - cells outside the detection window. This can help add context and - improve detection accuracy. This function returns the number of extra - HOG cells added onto the border of the HOG windows which are scanned by - detect(). - !*/ - - unsigned long get_cell_size ( - ) const; - /*! - ensures - - Returns the size of the HOG cells. Each HOG cell is square and contains - get_cell_size()*get_cell_size() pixels. - !*/ - - void set_cell_size ( - unsigned long new_cell_size - ); - /*! - requires - - new_cell_size > 0 - ensures - - #get_cell_size() == new_cell_size - - #is_loaded_with_image() == false - !*/ - - inline long get_num_dimensions ( - ) const; - /*! - ensures - - returns get_fhog_window_width()*get_fhog_window_height()*get_feature_extractor().get_num_planes() - (i.e. The number of features is equal to the size of the HOG window times - the number of planes output by the feature extractor. ) - !*/ - - inline unsigned long get_num_detection_templates ( - ) const { return 1; } - /*! - ensures - - returns 1. Note that this function is here only for compatibility with - the scan_image_pyramid object. Notionally, its return value indicates - that a scan_fhog_pyramid object is always ready to detect objects once - an image has been loaded. - !*/ - - inline unsigned long get_num_movable_components_per_detection_template ( - ) const { return 0; } - /*! - ensures - - returns 0. Note that this function is here only for compatibility with - the scan_image_pyramid object. Its return value means that this object - does not support using movable part models. - !*/ - - unsigned long get_max_pyramid_levels ( - ) const; - /*! - ensures - - returns the maximum number of image pyramid levels this object will use. - Note that #get_max_pyramid_levels() == 1 indicates that no image pyramid - will be used at all. That is, only the original image will be processed - and no lower scale versions will be created. - !*/ - - void set_max_pyramid_levels ( - unsigned long max_levels - ); - /*! - requires - - max_levels > 0 - ensures - - #get_max_pyramid_levels() == max_levels - !*/ - - void set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ); - /*! - requires - - width > 0 - - height > 0 - ensures - - #get_min_pyramid_layer_width() == width - - #get_min_pyramid_layer_height() == height - !*/ - - inline unsigned long get_min_pyramid_layer_width ( - ) const; - /*! - ensures - - returns the smallest allowable width of an image in the image pyramid. - All pyramids will always include the original input image, however, no - pyramid levels will be created which have a width smaller than the - value returned by this function. - !*/ - - inline unsigned long get_min_pyramid_layer_height ( - ) const; - /*! - ensures - - returns the smallest allowable height of an image in the image pyramid. - All pyramids will always include the original input image, however, no - pyramid levels will be created which have a height smaller than the - value returned by this function. - !*/ - - fhog_filterbank build_fhog_filterbank ( - const feature_vector_type& weights - ) const; - /*! - requires - - weights.size() >= get_num_dimensions() - ensures - - Creates and then returns a fhog_filterbank object FB such that: - - FB.get_num_dimensions() == get_num_dimensions() - - FB.get_filters() == the values in weights unpacked into get_feature_extractor().get_num_planes() filters. - - FB.num_separable_filters() == the number of separable filters necessary to - represent all the filters in FB.get_filters(). - !*/ - - class fhog_filterbank - { - /*! - WHAT THIS OBJECT REPRESENTS - This object represents a HOG filter bank. That is, the classifier that is - slid over a HOG pyramid is a set of get_feature_extractor().get_num_planes() - linear filters, each get_fhog_window_width() rows by get_fhog_window_height() - columns in size. This object contains that set of filters. - !*/ - - public: - long get_num_dimensions( - ) const; - /*! - ensures - - Returns the total number of values in the filters. - !*/ - - const std::vector<matrix<float> >& get_filters( - ) const; - /*! - ensures - - returns the set of HOG filters in this object. - !*/ - - unsigned long num_separable_filters( - ) const; - /*! - ensures - - returns the number of separable filters necessary to represent all - the filters in get_filters(). - !*/ - }; - - void detect ( - const fhog_filterbank& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - /*! - requires - - w.get_num_dimensions() == get_num_dimensions() - - is_loaded_with_image() == true - ensures - - Scans the HOG filter defined by w over the HOG pyramid that was populated - by the last call to load() and stores all object detections into #dets. - - for all valid i: - - #dets[i].second == The object box which produced this detection. This rectangle gives - the location of the detection. Note that the rectangle will have been converted back into - the original image input space. That is, if this detection was made at a low level in the - image pyramid then the object box will have been automatically mapped up the pyramid layers - to the original image space. Or in other words, if you plot #dets[i].second on top of the - image given to load() it will show up in the right place. - - #dets[i].first == The score for this detection. This value is equal to dot(w, feature vector - for this sliding window location). - - #dets[i].first >= thresh - - #dets will be sorted in descending order. (i.e. #dets[i].first >= #dets[j].first for all i, and j>i) - - Elements of w beyond index get_num_dimensions()-1 are ignored. I.e. only the first - get_num_dimensions() are used. - - Note that no form of non-max suppression is performed. If a window has a score >= thresh - then it is reported in #dets. - !*/ - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - - is_loaded_with_image() == true - ensures - - performs: detect(build_fhog_filterbank(w), dets, thresh) - !*/ - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - /*! - requires - - obj.num_parts() == 0 - - is_loaded_with_image() == true - - psi.size() >= get_num_dimensions() - (i.e. psi must have preallocated its memory before this function is called) - ensures - - This function allows you to determine the feature vector used for an - object detection output from detect(). Note that this vector is - added to psi. Note also that you can use get_full_object_detection() to - convert a rectangle from detect() into the needed full_object_detection. - - The dimensionality of the vector added to psi is get_num_dimensions(). This - means that elements of psi after psi(get_num_dimensions()-1) are not modified. - - Since scan_fhog_pyramid only searches a limited set of object locations, - not all possible rectangles can be output by detect(). So in the case - where obj.get_rect() could not arise from a call to detect(), this - function will map obj.get_rect() to the nearest possible rectangle and - then add the feature vector for the mapped rectangle into #psi. - - get_best_matching_rect(obj.get_rect()) == the rectangle obj.get_rect() - gets mapped to for feature extraction. - !*/ - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - /*! - ensures - - returns full_object_detection(rect) - (This function is here only for compatibility with the scan_image_pyramid - object) - !*/ - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - /*! - ensures - - Since scan_fhog_pyramid only searches a limited set of object locations, - not all possible rectangles can be represented. Therefore, this function - allows you to supply a rectangle and obtain the nearest possible - candidate object location rectangle. - !*/ - - double get_nuclear_norm_regularization_strength ( - ) const; - /*! - ensures - - If the number of separable filters in a fhog_filterbank is small then the - filter bank can be scanned over an image much faster than a normal set of - filters. Therefore, this object provides the option to encourage - machine learning methods that learn a HOG filter bank (i.e. - structural_object_detection_trainer) to select filter banks that have - this beneficial property. In particular, the value returned by - get_nuclear_norm_regularization_strength() is a multiplier on a nuclear - norm regularizer which will encourage the selection of filters that use a - small number of separable components. Larger values encourage tend to - give a smaller number of separable filters. - - if (get_nuclear_norm_regularization_strength() == 0) then - - This feature is disabled - - else - - A nuclear norm regularizer will be added when - structural_object_detection_trainer is used to learn a HOG filter - bank. Note that this can make the training process take - significantly longer (but can result in faster object detectors). - !*/ - - void set_nuclear_norm_regularization_strength ( - double strength - ); - /*! - requires - - strength >= 0 - ensures - - #get_nuclear_norm_regularization_strength() == strength - !*/ - - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void serialize ( - const scan_fhog_pyramid<T>& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void deserialize ( - scan_fhog_pyramid<T>& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename pyramid_type, - typename image_type - > - void evaluate_detectors ( - const std::vector<object_detector<scan_fhog_pyramid<pyramid_type>>>& detectors, - const image_type& img, - std::vector<rect_detection>& dets, - const double adjust_threshold = 0 - ); - /*! - requires - - image_type == is an implementation of array2d/array2d_kernel_abstract.h - - img contains some kind of pixel type. - (i.e. pixel_traits<typename image_type::type> is defined) - ensures - - This function runs each of the provided object_detector objects over img and - stores the resulting detections into #dets. Importantly, this function is - faster than running each detector individually because it computes the HOG - features only once and then reuses them for each detector. However, it is - important to note that this speedup is only possible if all the detectors use - the same cell_size parameter that determines how HOG features are computed. - If different cell_size values are used then this function will not be any - faster than running the detectors individually. - - This function applies non-max suppression individually to the output of each - detector. Therefore, the output is the same as if you ran each detector - individually and then concatenated the results. - - To be precise, this function performs object detection on the given image and - stores the detected objects into #dets. In particular, we will have that: - - #dets is sorted such that the highest confidence detections come first. - E.g. element 0 is the best detection, element 1 the next best, and so on. - - #dets.size() == the number of detected objects. - - #dets[i].detection_confidence == The strength of the i-th detection. - Larger values indicate that the detector is more confident that #dets[i] - is a correct detection rather than being a false alarm. Moreover, the - detection_confidence is equal to the detection value output by the - scanner minus the threshold value stored at the end of the weight vector. - - #dets[i].rect == the bounding box for the i-th detection. - - The detection #dets[i].rect was produced by detectors[#dets[i].weight_index]. - - The detection threshold is adjusted by having adjust_threshold added to it. - Therefore, an adjust_threshold value > 0 makes detecting objects harder while - a negative value makes it easier. Moreover, the following will be true for - all valid i: - - #dets[i].detection_confidence >= adjust_threshold - This means that, for example, you can obtain the maximum possible number of - detections by setting adjust_threshold equal to negative infinity. - - This function is threadsafe in the sense that multiple threads can call - evaluate_detectors() with the same instances of detectors and img without - requiring a mutex lock. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename pyramid_type, - typename image_type - > - std::vector<rectangle> evaluate_detectors ( - const std::vector<object_detector<scan_fhog_pyramid<pyramid_type>>>& detectors, - const image_type& img, - const double adjust_threshold = 0 - ); - /*! - requires - - image_type == is an implementation of array2d/array2d_kernel_abstract.h - - img contains some kind of pixel type. - (i.e. pixel_traits<typename image_type::type> is defined) - ensures - - This function just calls the above evaluate_detectors() routine and copies - the output dets into a vector<rectangle> object and returns it. Therefore, - this function is provided for convenience. - - This function is threadsafe in the sense that multiple threads can call - evaluate_detectors() with the same instances of detectors and img without - requiring a mutex lock. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_fHOG_PYRAMID_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/scan_image.h b/ml/dlib/dlib/image_processing/scan_image.h deleted file mode 100644 index 1a9c46eda..000000000 --- a/ml/dlib/dlib/image_processing/scan_image.h +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_iMAGE_Hh_ -#define DLIB_SCAN_iMAGE_Hh_ - -#include <vector> -#include <utility> -#include "scan_image_abstract.h" -#include "../matrix.h" -#include "../algs.h" -#include "../rand.h" -#include "../array2d.h" -#include "../image_transforms/spatial_filtering.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - - inline rectangle bounding_box_of_rects ( - const std::vector<std::pair<unsigned int, rectangle> >& rects, - const point& position - ) - /*! - ensures - - returns the smallest rectangle that contains all the - rectangles in rects. That is, returns the rectangle that - contains translate_rect(rects[i].second,position) for all valid i. - !*/ - { - rectangle rect; - - for (unsigned long i = 0; i < rects.size(); ++i) - { - rect += translate_rect(rects[i].second,position); - } - - return rect; - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - bool all_images_same_size ( - const image_array_type& images - ) - { - if (images.size() == 0) - return true; - - for (unsigned long i = 0; i < images.size(); ++i) - { - if (num_rows(images[0]) != num_rows(images[i]) || - num_columns(images[0]) != num_columns(images[i])) - return false; - } - - return true; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - double sum_of_rects_in_images ( - const image_array_type& images, - const std::vector<std::pair<unsigned int, rectangle> >& rects, - const point& position - ) - { - DLIB_ASSERT(all_images_same_size(images), - "\t double sum_of_rects_in_images()" - << "\n\t Invalid arguments given to this function." - << "\n\t all_images_same_size(images): " << all_images_same_size(images) - ); -#ifdef ENABLE_ASSERTS - for (unsigned long i = 0; i < rects.size(); ++i) - { - DLIB_ASSERT(rects[i].first < images.size(), - "\t double sum_of_rects_in_images()" - << "\n\t rects["<<i<<"].first must refer to a valid image." - << "\n\t rects["<<i<<"].first: " << rects[i].first - << "\n\t images.size(): " << images.size() - ); - } -#endif - - - typedef typename image_traits<typename image_array_type::type>::pixel_type pixel_type; - typedef typename promote<pixel_type>::type ptype; - - ptype temp = 0; - - for (unsigned long i = 0; i < rects.size(); ++i) - { - const typename image_array_type::type& img = images[rects[i].first]; - const rectangle rect = get_rect(img).intersect(translate_rect(rects[i].second,position)); - temp += sum(matrix_cast<ptype>(subm(mat(img), rect))); - } - - return static_cast<double>(temp); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - double sum_of_rects_in_images_movable_parts ( - const image_array_type& images, - const rectangle& window, - const std::vector<std::pair<unsigned int, rectangle> >& fixed_rects, - const std::vector<std::pair<unsigned int, rectangle> >& movable_rects, - const point& position - ) - { - DLIB_ASSERT(all_images_same_size(images) && center(window) == point(0,0), - "\t double sum_of_rects_in_images_movable_parts()" - << "\n\t Invalid arguments given to this function." - << "\n\t all_images_same_size(images): " << all_images_same_size(images) - << "\n\t center(window): " << center(window) - ); -#ifdef ENABLE_ASSERTS - for (unsigned long i = 0; i < fixed_rects.size(); ++i) - { - DLIB_ASSERT(fixed_rects[i].first < images.size(), - "\t double sum_of_rects_in_images_movable_parts()" - << "\n\t fixed_rects["<<i<<"].first must refer to a valid image." - << "\n\t fixed_rects["<<i<<"].first: " << fixed_rects[i].first - << "\n\t images.size(): " << images.size() - ); - } - for (unsigned long i = 0; i < movable_rects.size(); ++i) - { - DLIB_ASSERT(movable_rects[i].first < images.size(), - "\t double sum_of_rects_in_images_movable_parts()" - << "\n\t movable_rects["<<i<<"].first must refer to a valid image." - << "\n\t movable_rects["<<i<<"].first: " << movable_rects[i].first - << "\n\t images.size(): " << images.size() - ); - DLIB_ASSERT(center(movable_rects[i].second) == point(0,0), - "\t double sum_of_rects_in_images_movable_parts()" - << "\n\t movable_rects["<<i<<"].second: " << movable_rects[i].second - ); - } -#endif - typedef typename image_traits<typename image_array_type::type>::pixel_type pixel_type; - typedef typename promote<pixel_type>::type ptype; - - ptype temp = 0; - - // compute TOTAL_FIXED part - for (unsigned long i = 0; i < fixed_rects.size(); ++i) - { - const typename image_array_type::type& img = images[fixed_rects[i].first]; - const rectangle rect = get_rect(img).intersect(translate_rect(fixed_rects[i].second,position)); - temp += sum(matrix_cast<ptype>(subm(mat(img), rect))); - } - - if (images.size() > 0) - { - // compute TOTAL_MOVABLE part - array2d<ptype> tempimg(images[0].nr(), images[0].nc()); - for (unsigned long i = 0; i < movable_rects.size(); ++i) - { - const typename image_array_type::type& img = images[movable_rects[i].first]; - - sum_filter_assign(img, tempimg, movable_rects[i].second); - - const rectangle rect = get_rect(tempimg).intersect(translate_rect(window,position)); - if (rect.is_empty() == false) - temp += std::max(0,max(matrix_cast<ptype>(subm(mat(tempimg), rect)))); - } - } - - return static_cast<double>(temp); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_type - > - void find_points_above_thresh ( - std::vector<std::pair<double, point> >& dets, - const image_type& img_, - const double thresh, - const unsigned long max_dets - ) - { - const_image_view<image_type> img(img_); - typedef typename image_traits<image_type>::pixel_type ptype; - - dets.clear(); - if (max_dets == 0) - return; - - unsigned long count = 0; - dlib::rand rnd; - for (long r = 0; r < img.nr(); ++r) - { - for (long c = 0; c < img.nc(); ++c) - { - const ptype val = img[r][c]; - if (val >= thresh) - { - ++count; - - if (dets.size() < max_dets) - { - dets.push_back(std::make_pair(val, point(c,r))); - } - else - { - // The idea here is to cause us to randomly sample possible detection - // locations throughout the image rather than just stopping the detection - // procedure once we hit the max_dets limit. So this method will result - // in a random subsample of all the detections >= thresh being in dets - // at the end of scan_image(). - const unsigned long random_index = rnd.get_random_32bit_number()%count; - if (random_index < dets.size()) - { - dets[random_index] = std::make_pair(val, point(c,r)); - } - } - } - } - } - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - void scan_image ( - std::vector<std::pair<double, point> >& dets, - const image_array_type& images, - const std::vector<std::pair<unsigned int, rectangle> >& rects, - const double thresh, - const unsigned long max_dets - ) - { - DLIB_ASSERT(images.size() > 0 && rects.size() > 0 && all_images_same_size(images), - "\t void scan_image()" - << "\n\t Invalid arguments given to this function." - << "\n\t images.size(): " << images.size() - << "\n\t rects.size(): " << rects.size() - << "\n\t all_images_same_size(images): " << all_images_same_size(images) - ); -#ifdef ENABLE_ASSERTS - for (unsigned long i = 0; i < rects.size(); ++i) - { - DLIB_ASSERT(rects[i].first < images.size(), - "\t void scan_image()" - << "\n\t rects["<<i<<"].first must refer to a valid image." - << "\n\t rects["<<i<<"].first: " << rects[i].first - << "\n\t images.size(): " << images.size() - ); - } -#endif - - - - - typedef typename image_traits<typename image_array_type::type>::pixel_type pixel_type; - typedef typename promote<pixel_type>::type ptype; - - array2d<ptype> accum(images[0].nr(), images[0].nc()); - assign_all_pixels(accum, 0); - - for (unsigned long i = 0; i < rects.size(); ++i) - sum_filter(images[rects[i].first], accum, rects[i].second); - - find_points_above_thresh(dets, accum, thresh, max_dets); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - void scan_image_movable_parts ( - std::vector<std::pair<double, point> >& dets, - const image_array_type& images, - const rectangle& window, - const std::vector<std::pair<unsigned int, rectangle> >& fixed_rects, - const std::vector<std::pair<unsigned int, rectangle> >& movable_rects, - const double thresh, - const unsigned long max_dets - ) - { - DLIB_ASSERT(images.size() > 0 && all_images_same_size(images) && - center(window) == point(0,0) && window.area() > 0, - "\t void scan_image_movable_parts()" - << "\n\t Invalid arguments given to this function." - << "\n\t all_images_same_size(images): " << all_images_same_size(images) - << "\n\t center(window): " << center(window) - << "\n\t window.area(): " << window.area() - << "\n\t images.size(): " << images.size() - ); -#ifdef ENABLE_ASSERTS - for (unsigned long i = 0; i < fixed_rects.size(); ++i) - { - DLIB_ASSERT(fixed_rects[i].first < images.size(), - "\t void scan_image_movable_parts()" - << "\n\t Invalid arguments given to this function." - << "\n\t fixed_rects["<<i<<"].first must refer to a valid image." - << "\n\t fixed_rects["<<i<<"].first: " << fixed_rects[i].first - << "\n\t images.size(): " << images.size() - ); - } - for (unsigned long i = 0; i < movable_rects.size(); ++i) - { - DLIB_ASSERT(movable_rects[i].first < images.size(), - "\t void scan_image_movable_parts()" - << "\n\t Invalid arguments given to this function." - << "\n\t movable_rects["<<i<<"].first must refer to a valid image." - << "\n\t movable_rects["<<i<<"].first: " << movable_rects[i].first - << "\n\t images.size(): " << images.size() - ); - DLIB_ASSERT(center(movable_rects[i].second) == point(0,0) && - movable_rects[i].second.area() > 0, - "\t void scan_image_movable_parts()" - << "\n\t Invalid arguments given to this function." - << "\n\t movable_rects["<<i<<"].second: " << movable_rects[i].second - << "\n\t movable_rects["<<i<<"].second.area(): " << movable_rects[i].second.area() - ); - } -#endif - - if (movable_rects.size() == 0 && fixed_rects.size() == 0) - return; - - typedef typename image_traits<typename image_array_type::type>::pixel_type pixel_type; - typedef typename promote<pixel_type>::type ptype; - - array2d<ptype> accum(images[0].nr(), images[0].nc()); - assign_all_pixels(accum, 0); - - for (unsigned long i = 0; i < fixed_rects.size(); ++i) - sum_filter(images[fixed_rects[i].first], accum, fixed_rects[i].second); - - array2d<ptype> temp(accum.nr(), accum.nc()); - for (unsigned long i = 0; i < movable_rects.size(); ++i) - { - const rectangle rect = movable_rects[i].second; - sum_filter_assign(images[movable_rects[i].first], temp, rect); - max_filter(temp, accum, window.width(), window.height(), 0); - } - - find_points_above_thresh(dets, accum, thresh, max_dets); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_iMAGE_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/scan_image_abstract.h b/ml/dlib/dlib/image_processing/scan_image_abstract.h deleted file mode 100644 index fe2fc51ac..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_abstract.h +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_iMAGE_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_iMAGE_ABSTRACT_Hh_ - -#include <vector> -#include <utility> -#include "../algs.h" -#include "../image_processing/generic_image.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - bool all_images_same_size ( - const image_array_type& images - ); - /*! - requires - - image_array_type == an implementation of array/array_kernel_abstract.h - - image_array_type::type == an image object that implements the interface - defined in dlib/image_processing/generic_image.h - ensures - - if (all elements of images have the same dimensions (i.e. - for all i and j: get_rect(images[i]) == get_rect(images[j]))) then - - returns true - - else - - returns false - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - double sum_of_rects_in_images ( - const image_array_type& images, - const std::vector<std::pair<unsigned int, rectangle> >& rects, - const point& position - ); - /*! - requires - - image_array_type == an implementation of array/array_kernel_abstract.h - - image_array_type::type == an image object that implements the interface - defined in dlib/image_processing/generic_image.h. Moreover, these objects must - contain a scalar pixel type (e.g. int rather than rgb_pixel) - - all_images_same_size(images) == true - - for all valid i: rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - ensures - - returns the sum of the pixels inside the given rectangles. To be precise, - let RECT_SUM[i] = sum of pixels inside the rectangle translate_rect(rects[i].second, position) - from the image images[rects[i].first]. Then this function returns the - sum of RECT_SUM[i] for all the valid values of i. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - double sum_of_rects_in_images_movable_parts ( - const image_array_type& images, - const rectangle& window, - const std::vector<std::pair<unsigned int, rectangle> >& fixed_rects, - const std::vector<std::pair<unsigned int, rectangle> >& movable_rects, - const point& position - ); - /*! - requires - - image_array_type == an implementation of array/array_kernel_abstract.h - - image_array_type::type == an image object that implements the interface - defined in dlib/image_processing/generic_image.h. Moreover, these objects must - contain a scalar pixel type (e.g. int rather than rgb_pixel) - - all_images_same_size(images) == true - - center(window) == point(0,0) - - for all valid i: - - fixed_rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - - for all valid i: - - movable_rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - - center(movable_rects[i].second) == point(0,0) - ensures - - returns the sum of the pixels inside fixed_rects as well as the sum of the pixels - inside movable_rects when these latter rectangles are placed at their highest - scoring locations inside the given window. To be precise: - - let RECT_SUM(r,x) = sum of pixels inside the rectangle translate_rect(r.second, x) - from the image images[r.first]. - - let WIN_MAX(i) = The maximum value of RECT_SUM(movable_rects[i],X) when maximizing - over all the X such that translate_rect(window,position).contains(X) == true. - - - let TOTAL_FIXED == sum over all elements R in fixed_rects of: RECT_SUM(R,position) - - let TOTAL_MOVABLE == sum over all valid i of: max(WIN_MAX(i), 0) - - Then this function returns TOTAL_FIXED + TOTAL_MOVABLE. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_type - > - void find_points_above_thresh ( - std::vector<std::pair<double, point> >& dets, - const image_type& img, - const double thresh, - const unsigned long max_dets - ); - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h. Moreover, these it must contain a - scalar pixel type (e.g. int rather than rgb_pixel) - ensures - - #dets == a list of points from img which had pixel values >= thresh. - - Specifically, we have: - - #dets.size() <= max_dets - (note that dets is cleared before new detections are added by find_points_above_thresh()) - - for all valid i: - - #dets[i].first == img[#dets[i].second.y()][#dets[i].second.x()] - (i.e. the first field contains the value of the pixel at this detection location) - - #dets[i].first >= thresh - - if (there are more than max_dets locations that pass the above threshold test) then - - #dets == a random subsample of all the locations which passed the threshold - test. - - else - - #dets == all the points which passed the threshold test. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - void scan_image ( - std::vector<std::pair<double, point> >& dets, - const image_array_type& images, - const std::vector<std::pair<unsigned int, rectangle> >& rects, - const double thresh, - const unsigned long max_dets - ); - /*! - requires - - image_array_type == an implementation of array/array_kernel_abstract.h - - image_array_type::type == an image object that implements the interface - defined in dlib/image_processing/generic_image.h. Moreover, these objects must - contain a scalar pixel type (e.g. int rather than rgb_pixel) - - images.size() > 0 - - rects.size() > 0 - - all_images_same_size(images) == true - - for all valid i: rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - ensures - - slides the set of rectangles over the image space and reports the locations - which give a sum bigger than thresh. - - Specifically, we have: - - #dets.size() <= max_dets - (note that dets is cleared before new detections are added by scan_image()) - - for all valid i: - - #dets[i].first == sum_of_rects_in_images(images,rects,#dets[i].second) >= thresh - - if (there are more than max_dets locations that pass the threshold test) then - - #dets == a random subsample of all the locations which passed the threshold - test. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array_type - > - void scan_image_movable_parts ( - std::vector<std::pair<double, point> >& dets, - const image_array_type& images, - const rectangle& window, - const std::vector<std::pair<unsigned int, rectangle> >& fixed_rects, - const std::vector<std::pair<unsigned int, rectangle> >& movable_rects, - const double thresh, - const unsigned long max_dets - ); - /*! - requires - - image_array_type == an implementation of array/array_kernel_abstract.h - - image_array_type::type == an image object that implements the interface - defined in dlib/image_processing/generic_image.h. Moreover, these objects must - contain a scalar pixel type (e.g. int rather than rgb_pixel) - - images.size() > 0 - - all_images_same_size(images) == true - - center(window) == point(0,0) - - window.area() > 0 - - for all valid i: - - fixed_rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - - for all valid i: - - movable_rects[i].first < images.size() - (i.e. all the rectangles must reference valid elements of images) - - center(movable_rects[i].second) == point(0,0) - - movable_rects[i].second.area() > 0 - ensures - - Scans the given window over the images and reports the locations with a score bigger - than thresh. - - Specifically, we have: - - #dets.size() <= max_dets - (note that dets is cleared before new detections are added by scan_image_movable_parts()) - - for all valid i: - - #dets[i].first == sum_of_rects_in_images_movable_parts(images, - window, - fixed_rects, - movable_rects, - #dets[i].second) >= thresh - - if (there are more than max_dets locations that pass the above threshold test) then - - #dets == a random subsample of all the locations which passed the threshold - test. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_iMAGE_ABSTRACT_Hh_ - - - diff --git a/ml/dlib/dlib/image_processing/scan_image_boxes.h b/ml/dlib/dlib/image_processing/scan_image_boxes.h deleted file mode 100644 index f4549565c..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_boxes.h +++ /dev/null @@ -1,630 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_IMAGE_bOXES_Hh_ -#define DLIB_SCAN_IMAGE_bOXES_Hh_ - -#include "scan_image_boxes_abstract.h" -#include "../matrix.h" -#include "../geometry.h" -#include "../array2d.h" -#include <vector> -#include "../image_processing/full_object_detection.h" -#include "../image_transforms.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class default_box_generator - { - public: - template <typename image_type> - void operator() ( - const image_type& img, - std::vector<rectangle>& rects - ) const - { - rects.clear(); - find_candidate_object_locations(img, rects); - } - }; - - inline void serialize(const default_box_generator&, std::ostream& ) {} - inline void deserialize(default_box_generator&, std::istream& ) {} - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator = default_box_generator - > - class scan_image_boxes : noncopyable - { - - public: - - typedef matrix<double,0,1> feature_vector_type; - - typedef Feature_extractor_type feature_extractor_type; - typedef Box_generator box_generator; - - scan_image_boxes ( - ); - - template < - typename image_type - > - void load ( - const image_type& img - ); - - inline bool is_loaded_with_image ( - ) const; - - inline void copy_configuration( - const feature_extractor_type& fe - ); - - inline void copy_configuration( - const box_generator& bg - ); - - const box_generator& get_box_generator ( - ) const { return detect_boxes; } - - const Feature_extractor_type& get_feature_extractor ( - ) const { return feats; } - - inline void copy_configuration ( - const scan_image_boxes& item - ); - - inline long get_num_dimensions ( - ) const; - - unsigned long get_num_spatial_pyramid_levels ( - ) const; - - void set_num_spatial_pyramid_levels ( - unsigned long levels - ); - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - /*! - requires - - is_loaded_with_image() == true - !*/ - - inline unsigned long get_num_detection_templates ( - ) const { return 1; } - - inline unsigned long get_num_movable_components_per_detection_template ( - ) const { return 0; } - - template <typename T, typename U> - friend void serialize ( - const scan_image_boxes<T,U>& item, - std::ostream& out - ); - - template <typename T, typename U> - friend void deserialize ( - scan_image_boxes<T,U>& item, - std::istream& in - ); - - private: - static bool compare_pair_rect ( - const std::pair<double, rectangle>& a, - const std::pair<double, rectangle>& b - ) - { - return a.first < b.first; - } - - void test_coordinate_transforms() - { - for (long x = -10; x <= 10; x += 10) - { - for (long y = -10; y <= 10; y += 10) - { - const rectangle rect = centered_rect(x,y,5,6); - rectangle a; - - a = feats.image_to_feat_space(rect); - if (a.width() > 10000000 || a.height() > 10000000 ) - { - DLIB_CASSERT(false, "The image_to_feat_space() routine is outputting rectangles of an implausibly " - << "\nlarge size. This means there is probably a bug in your feature extractor."); - } - a = feats.feat_to_image_space(rect); - if (a.width() > 10000000 || a.height() > 10000000 ) - { - DLIB_CASSERT(false, "The feat_to_image_space() routine is outputting rectangles of an implausibly " - << "\nlarge size. This means there is probably a bug in your feature extractor."); - } - } - } - - } - - static void add_grid_rects ( - std::vector<rectangle>& rects, - const rectangle& object_box, - unsigned int cells_x, - unsigned int cells_y - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(cells_x > 0 && cells_y > 0, - "\t void add_grid_rects()" - << "\n\t The number of cells along a dimension can't be zero. " - << "\n\t cells_x: " << cells_x - << "\n\t cells_y: " << cells_y - ); - - const matrix_range_exp<double>& x = linspace(object_box.left(), object_box.right(), cells_x+1); - const matrix_range_exp<double>& y = linspace(object_box.top(), object_box.bottom(), cells_y+1); - - for (long j = 0; j+1 < y.size(); ++j) - { - for (long i = 0; i+1 < x.size(); ++i) - { - const dlib::vector<double,2> tl(x(i),y(j)); - const dlib::vector<double,2> br(x(i+1),y(j+1)); - rects.push_back(rectangle(tl,br)); - } - } - } - - void get_feature_extraction_regions ( - const rectangle& rect, - std::vector<rectangle>& regions - ) const - /*! - ensures - - #regions.size() is always the same number no matter what the input is. The - regions also have a consistent ordering. - - all the output rectangles are contained within rect. - !*/ - { - regions.clear(); - - for (unsigned int l = 1; l <= num_spatial_pyramid_levels; ++l) - { - const int cells = (int)std::pow(2.0, l-1.0); - add_grid_rects(regions, rect, cells, cells); - } - } - - unsigned int get_num_components_per_detection_template( - ) const - { - return (unsigned int)(std::pow(4.0,(double)num_spatial_pyramid_levels)-1)/3; - } - - feature_extractor_type feats; - std::vector<rectangle> search_rects; - bool loaded_with_image; - unsigned int num_spatial_pyramid_levels; - box_generator detect_boxes; - - const long box_sizedims; - const long box_maxsize; - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void serialize ( - const scan_image_boxes<T,U>& item, - std::ostream& out - ) - { - int version = 1; - serialize(version, out); - serialize(item.feats, out); - serialize(item.search_rects, out); - serialize(item.loaded_with_image, out); - serialize(item.num_spatial_pyramid_levels, out); - serialize(item.detect_boxes, out); - serialize(item.get_num_dimensions(), out); - } - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void deserialize ( - scan_image_boxes<T,U>& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version != 1) - throw serialization_error("Unsupported version found when deserializing a scan_image_boxes object."); - - deserialize(item.feats, in); - deserialize(item.search_rects, in); - deserialize(item.loaded_with_image, in); - deserialize(item.num_spatial_pyramid_levels, in); - deserialize(item.detect_boxes, in); - - // When developing some feature extractor, it's easy to accidentally change its - // number of dimensions and then try to deserialize data from an older version of - // your extractor into the current code. This check is here to catch that kind of - // user error. - long dims; - deserialize(dims, in); - if (item.get_num_dimensions() != dims) - throw serialization_error("Number of dimensions in serialized scan_image_boxes doesn't match the expected number."); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// scan_image_boxes member functions -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - scan_image_boxes<Feature_extractor_type,Box_generator>:: - scan_image_boxes ( - ) : - loaded_with_image(false), - num_spatial_pyramid_levels(3), - box_sizedims(20), - box_maxsize(1200) - { - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - template < - typename image_type - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - load ( - const image_type& img - ) - { - feats.load(img); - detect_boxes(img, search_rects); - loaded_with_image = true; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - bool scan_image_boxes<Feature_extractor_type,Box_generator>:: - is_loaded_with_image ( - ) const - { - return loaded_with_image; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - copy_configuration( - const feature_extractor_type& fe - ) - { - test_coordinate_transforms(); - feats.copy_configuration(fe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - copy_configuration( - const box_generator& bg - ) - { - detect_boxes = bg; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - copy_configuration ( - const scan_image_boxes& item - ) - { - feats.copy_configuration(item.feats); - detect_boxes = item.detect_boxes; - num_spatial_pyramid_levels = item.num_spatial_pyramid_levels; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - unsigned long scan_image_boxes<Feature_extractor_type,Box_generator>:: - get_num_spatial_pyramid_levels ( - ) const - { - return num_spatial_pyramid_levels; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - set_num_spatial_pyramid_levels ( - unsigned long levels - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(levels > 0, - "\t void scan_image_boxes::set_num_spatial_pyramid_levels()" - << "\n\t Invalid inputs were given to this function " - << "\n\t levels: " << levels - << "\n\t this: " << this - ); - - - num_spatial_pyramid_levels = levels; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - long scan_image_boxes<Feature_extractor_type,Box_generator>:: - get_num_dimensions ( - ) const - { - return feats.get_num_dimensions()*get_num_components_per_detection_template() + box_sizedims*2; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - w.size() >= get_num_dimensions(), - "\t void scan_image_boxes::detect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t w.size(): " << w.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - dets.clear(); - - array<integral_image_generic<double> > saliency_images(get_num_components_per_detection_template()); - - array2d<double> temp_img(feats.nr(), feats.nc()); - - // build saliency images - for (unsigned long i = 0; i < saliency_images.size(); ++i) - { - const unsigned long offset = 2*box_sizedims + feats.get_num_dimensions()*i; - - // make the basic saliency image for the i-th feature extraction region - for (long r = 0; r < feats.nr(); ++r) - { - for (long c = 0; c < feats.nc(); ++c) - { - const typename feature_extractor_type::descriptor_type& descriptor = feats(r,c); - - double sum = 0; - for (unsigned long k = 0; k < descriptor.size(); ++k) - { - sum += w(descriptor[k].first + offset)*descriptor[k].second; - } - temp_img[r][c] = sum; - } - } - - // now convert base saliency image into final integral image - saliency_images[i].load(temp_img); - } - - - // now search the saliency images - std::vector<rectangle> regions; - const rectangle bounds = get_rect(feats); - for (unsigned long i = 0; i < search_rects.size(); ++i) - { - const rectangle rect = feats.image_to_feat_space(search_rects[i]).intersect(bounds); - if (rect.is_empty()) - continue; - get_feature_extraction_regions(rect, regions); - double score = 0; - for (unsigned long k = 0; k < regions.size(); ++k) - { - score += saliency_images[k].get_sum_of_area(regions[k]); - } - const double width = search_rects[i].width(); - const double height = search_rects[i].height(); - - score += dot(linpiece(width, linspace(0, box_maxsize, box_sizedims+1)), rowm(w, range(0,box_sizedims-1))); - score += dot(linpiece(height, linspace(0, box_maxsize, box_sizedims+1)), rowm(w, range(box_sizedims,2*box_sizedims-1))); - - if (score >= thresh) - { - dets.push_back(std::make_pair(score, search_rects[i])); - } - } - - std::sort(dets.rbegin(), dets.rend(), compare_pair_rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - const rectangle scan_image_boxes<Feature_extractor_type,Box_generator>:: - get_best_matching_rect ( - const rectangle& rect - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image(), - "\t const rectangle scan_image_boxes::get_best_matching_rect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t this: " << this - ); - - - double best_score = -1; - rectangle best_rect; - for (unsigned long i = 0; i < search_rects.size(); ++i) - { - const double score = (rect.intersect(search_rects[i])).area()/(double)(rect+search_rects[i]).area(); - if (score > best_score) - { - best_score = score; - best_rect = search_rects[i]; - } - } - return best_rect; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - full_object_detection scan_image_boxes<Feature_extractor_type,Box_generator>:: - get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& /*w*/ - ) const - { - return full_object_detection(rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void scan_image_boxes<Feature_extractor_type,Box_generator>:: - get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - psi.size() >= get_num_dimensions() && - obj.num_parts() == 0, - "\t void scan_image_boxes::get_feature_vector()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t psi.size(): " << psi.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t obj.num_parts(): " << obj.num_parts() - << "\n\t this: " << this - ); - - - - const rectangle best_rect = get_best_matching_rect(obj.get_rect()); - const rectangle mapped_rect = feats.image_to_feat_space(best_rect).intersect(get_rect(feats)); - if (mapped_rect.is_empty()) - return; - - std::vector<rectangle> regions; - get_feature_extraction_regions(mapped_rect, regions); - - // pull features out of all the boxes in regions. - for (unsigned long j = 0; j < regions.size(); ++j) - { - const rectangle rect = regions[j]; - - const unsigned long template_region_id = j; - const unsigned long offset = box_sizedims*2 + feats.get_num_dimensions()*template_region_id; - for (long r = rect.top(); r <= rect.bottom(); ++r) - { - for (long c = rect.left(); c <= rect.right(); ++c) - { - const typename feature_extractor_type::descriptor_type& descriptor = feats(r,c); - for (unsigned long k = 0; k < descriptor.size(); ++k) - { - psi(descriptor[k].first + offset) += descriptor[k].second; - } - } - } - } - - const double width = best_rect.width(); - const double height = best_rect.height(); - set_rowm(psi, range(0,box_sizedims-1)) += linpiece(width, linspace(0, box_maxsize, box_sizedims+1)); - set_rowm(psi, range(box_sizedims,box_sizedims*2-1)) += linpiece(height, linspace(0, box_maxsize, box_sizedims+1)); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMAGE_bOXES_Hh_ - - - diff --git a/ml/dlib/dlib/image_processing/scan_image_boxes_abstract.h b/ml/dlib/dlib/image_processing/scan_image_boxes_abstract.h deleted file mode 100644 index e2f16aa76..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_boxes_abstract.h +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_IMAGE_bOXES_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_IMAGE_bOXES_ABSTRACT_Hh_ - -#include "../matrix.h" -#include "../geometry.h" -#include "../image_processing.h" -#include "../array2d.h" -#include "full_object_detection_abstract.h" -#include "../image_transforms/segment_image_abstract.h" -#include <vector> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class default_box_generator - { - /*! - WHAT THIS OBJECT REPRESENTS - This is a function object that takes in an image and outputs a set of - candidate object locations. It is also the default box generator used by - the scan_image_boxes object defined below. - !*/ - - public: - - template <typename image_type> - void operator() ( - const image_type& img, - std::vector<rectangle>& rects - ) const - /*! - ensures - - #rects == the set of candidate object locations which should be searched - inside img. That is, these are the rectangles which might contain - objects of interest within the given image. - !*/ - { - rects.clear(); - find_candidate_object_locations(img, rects); - } - }; - - inline void serialize (const default_box_generator&, std::ostream& ) {} - inline void deserialize( default_box_generator&, std::istream& ) {} - /*! - ensures - - provides serialization support. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator = default_box_generator - > - class scan_image_boxes : noncopyable - { - /*! - REQUIREMENTS ON Feature_extractor_type - - must be an object with an interface compatible with the hashed_feature_image - object defined in dlib/image_keypoint/hashed_feature_image_abstract.h or - with the nearest_neighbor_feature_image object defined in - dlib/image_keypoint/nearest_neighbor_feature_image_abstract.h - - REQUIREMENTS ON Box_generator - - must be an object with an interface compatible with the - default_box_generator object defined at the top of this file. - - INITIAL VALUE - - get_num_spatial_pyramid_levels() == 3 - - is_loaded_with_image() == false - - WHAT THIS OBJECT REPRESENTS - This object is a tool for running a classifier over an image with the goal - of localizing each object present. The localization is in the form of the - bounding box around each object of interest. - - Unlike the scan_image_pyramid object which scans a fixed sized window over - an image pyramid, the scan_image_boxes tool allows you to define your own - list of "candidate object locations" which should be evaluated. This is - simply a list of rectangle objects which might contain objects of interest. - The scan_image_boxes object will then evaluate the classifier at each of - these locations and return the subset of rectangles which appear to have - objects in them. The candidate object location generation is provided by - the Box_generator that is passed in as a template argument. - - This object can also be understood as a general tool for implementing the - spatial pyramid models described in the paper: - Beyond Bags of Features: Spatial Pyramid Matching for Recognizing - Natural Scene Categories by Svetlana Lazebnik, Cordelia Schmid, - and Jean Ponce - - - The classifiers used by this object have three parts: - 1. The underlying feature extraction provided by Feature_extractor_type - objects, which associate a vector with each location in an image. - - 2. A rule for extracting a feature vector from a candidate object - location. In this object we use the spatial pyramid matching method. - This means we cut an object's detection window into a set of "feature - extraction regions" and extract a bag-of-words vector from each - before finally concatenating them to form the final feature vector - representing the entire object window. The set of feature extraction - regions can be configured by the user by calling - set_num_spatial_pyramid_levels(). To be a little more precise, the - feature vector for a candidate object window is defined as follows: - - Let N denote the number of feature extraction zones. - - Let M denote the dimensionality of the vectors output by - Feature_extractor_type objects. - - Let F(i) == the M dimensional vector which is the sum of all - vectors given by our Feature_extractor_type object inside the - i-th feature extraction zone. So this is notionally a - bag-of-words vector from the i-th zone. - - Then the feature vector for an object window is an M*N - dimensional vector [F(1) F(2) F(3) ... F(N)] (i.e. it is a - concatenation of the N vectors). This feature vector can be - thought of as a collection of N bags-of-words, each bag coming - from a spatial location determined by one of the feature - extraction zones. - - 3. A weight vector and a threshold value. The dot product between the - weight vector and the feature vector for a candidate object location - gives the score of the location. If this score is greater than the - threshold value then the candidate object location is output as a - detection. - - THREAD SAFETY - Concurrent access to an instance of this object is not safe and should be - protected by a mutex lock except for the case where you are copying the - configuration (via copy_configuration()) of a scan_image_boxes object to - many other threads. In this case, it is safe to copy the configuration of - a shared object so long as no other operations are performed on it. - !*/ - - public: - - typedef matrix<double,0,1> feature_vector_type; - - typedef Feature_extractor_type feature_extractor_type; - typedef Box_generator box_generator; - - scan_image_boxes ( - ); - /*! - ensures - - this object is properly initialized - !*/ - - template < - typename image_type - > - void load ( - const image_type& img - ); - /*! - requires - - image_type must be a type with the following properties: - - image_type objects can be loaded into Feature_extractor_type - objects via Feature_extractor_type::load(). - - image_type objects can be passed to the first argument of - Box_generator::operator() - ensures - - #is_loaded_with_image() == true - - This object is ready to run a classifier over img to detect object - locations. Call detect() to do this. - !*/ - - bool is_loaded_with_image ( - ) const; - /*! - ensures - - returns true if this object has been loaded with an image to process and - false otherwise. - !*/ - - const feature_extractor_type& get_feature_extractor ( - ) const; - /*! - ensures - - returns a const reference to the feature_extractor_type object used - internally for local feature extraction. - !*/ - - void copy_configuration( - const feature_extractor_type& fe - ); - /*! - ensures - - This function performs the equivalent of - get_feature_extractor().copy_configuration(fe) (i.e. this function allows - you to configure the parameters of the underlying feature extractor used - by a scan_image_boxes object) - !*/ - - void copy_configuration( - const box_generator& bg - ); - /*! - ensures - - #get_box_generator() == bg - (i.e. this function allows you to configure the parameters of the - underlying box generator used by a scan_image_boxes object) - !*/ - - const box_generator& get_box_generator ( - ) const; - /*! - ensures - - returns the box_generator used by this object to generate candidate - object locations. - !*/ - - void copy_configuration ( - const scan_image_boxes& item - ); - /*! - ensures - - Copies all the state information of item into *this, except for state - information populated by load(). More precisely, given two scan_image_boxes - objects S1 and S2, the following sequence of instructions should always - result in both of them having the exact same state: - S2.copy_configuration(S1); - S1.load(img); - S2.load(img); - !*/ - - long get_num_dimensions ( - ) const; - /*! - ensures - - returns the number of dimensions in the feature vector for a candidate - object location. This value is the dimensionality of the underlying - feature vectors produced by Feature_extractor_type times the number of - feature extraction regions used. Note that the number of feature - extraction regions used is a function of - get_num_spatial_pyramid_levels(). - !*/ - - unsigned long get_num_spatial_pyramid_levels ( - ) const; - /*! - ensures - - returns the number of layers in the spatial pyramid. For example, if - this function returns 1 then it means we use a simple bag-of-words - representation over the whole object window. If it returns 2 then it - means the feature representation is the concatenation of 5 bag-of-words - vectors, one from the entire object window and 4 others from 4 different - parts of the object window. If it returns 3 then there are 1+4+16 - bag-of-words vectors concatenated together in the feature representation, - and so on. - !*/ - - void set_num_spatial_pyramid_levels ( - unsigned long levels - ); - /*! - requires - - levels > 0 - ensures - - #get_num_spatial_pyramid_levels() == levels - !*/ - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - - is_loaded_with_image() == true - ensures - - Scans over all the candidate object locations as discussed in the WHAT - THIS OBJECT REPRESENTS section and stores all detections into #dets. - - for all valid i: - - #dets[i].second == The candidate object location which produced this - detection. This rectangle gives the location of the detection. - - #dets[i].first == The score for this detection. This value is equal - to dot(w, feature vector for this candidate object location). - - #dets[i].first >= thresh - - #dets will be sorted in descending order. - (i.e. #dets[i].first >= #dets[j].first for all i, and j>i) - - Elements of w beyond index get_num_dimensions()-1 are ignored. I.e. only - the first get_num_dimensions() are used. - - Note that no form of non-max suppression is performed. If a locations - has a score >= thresh then it is reported in #dets. - !*/ - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - /*! - requires - - obj.num_parts() == 0 - - is_loaded_with_image() == true - - psi.size() >= get_num_dimensions() - (i.e. psi must have preallocated its memory before this function is called) - ensures - - This function allows you to determine the feature vector used for a - candidate object location output from detect(). Note that this vector is - added to psi. Note also that you must use get_full_object_detection() to - convert a rectangle from detect() into the needed full_object_detection. - - The dimensionality of the vector added to psi is get_num_dimensions(). This - means that elements of psi after psi(get_num_dimensions()-1) are not modified. - - Since scan_image_boxes only searches a limited set of object locations, - not all possible rectangles can be output by detect(). So in the case - where obj.get_rect() could not arise from a call to detect(), this - function will map obj.get_rect() to the nearest possible rectangle and - then add the feature vector for the mapped rectangle into #psi. - - get_best_matching_rect(obj.get_rect()) == the rectangle obj.get_rect() - gets mapped to for feature extraction. - !*/ - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - /*! - ensures - - returns full_object_detection(rect) - (This function is here only for compatibility with the scan_image_pyramid - object) - !*/ - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - /*! - requires - - is_loaded_with_image() == true - ensures - - Since scan_image_boxes only searches a limited set of object locations, - not all possible rectangles can be represented. Therefore, this function - allows you to supply a rectangle and obtain the nearest possible - candidate object location rectangle. - !*/ - - unsigned long get_num_detection_templates ( - ) const { return 1; } - /*! - ensures - - returns 1. Note that this function is here only for compatibility with - the scan_image_pyramid object. Notionally, its return value indicates - that a scan_image_boxes object is always ready to detect objects once - an image has been loaded. - !*/ - - unsigned long get_num_movable_components_per_detection_template ( - ) const { return 0; } - /*! - ensures - - returns 0. Note that this function is here only for compatibility with - the scan_image_pyramid object. Its return value means that this object - does not support using movable part models. - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type, - typename Box_generator - > - void serialize ( - const scan_image_boxes<Feature_extractor_type,Box_generator>& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - template < - typename Feature_extractor_type, - typename Box_generator - > - void deserialize ( - scan_image_boxes<Feature_extractor_type,Box_generator>& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMAGE_bOXES_ABSTRACT_Hh_ - diff --git a/ml/dlib/dlib/image_processing/scan_image_custom.h b/ml/dlib/dlib/image_processing/scan_image_custom.h deleted file mode 100644 index 29b969fca..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_custom.h +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_IMAGE_CuSTOM_Hh_ -#define DLIB_SCAN_IMAGE_CuSTOM_Hh_ - -#include "scan_image_custom_abstract.h" -#include "../matrix.h" -#include "../geometry.h" -#include <vector> -#include "../image_processing/full_object_detection.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - class scan_image_custom : noncopyable - { - - public: - - typedef matrix<double,0,1> feature_vector_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_image_custom ( - ); - - template < - typename image_type - > - void load ( - const image_type& img - ); - - inline bool is_loaded_with_image ( - ) const; - - inline void copy_configuration( - const feature_extractor_type& fe - ); - - const Feature_extractor_type& get_feature_extractor ( - ) const { return feats; } - - inline void copy_configuration ( - const scan_image_custom& item - ); - - inline long get_num_dimensions ( - ) const; - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - - inline unsigned long get_num_detection_templates ( - ) const { return 1; } - - inline unsigned long get_num_movable_components_per_detection_template ( - ) const { return 0; } - - template <typename T> - friend void serialize ( - const scan_image_custom<T>& item, - std::ostream& out - ); - - template <typename T> - friend void deserialize ( - scan_image_custom<T>& item, - std::istream& in - ); - - private: - static bool compare_pair_rect ( - const std::pair<double, rectangle>& a, - const std::pair<double, rectangle>& b - ) - { - return a.first < b.first; - } - - - DLIB_MAKE_HAS_MEMBER_FUNCTION_TEST( - has_compute_object_score, - double, - compute_object_score, - ( const matrix<double,0,1>& w, const rectangle& obj) const - ); - - template <typename fe_type> - typename enable_if<has_compute_object_score<fe_type> >::type compute_all_rect_scores ( - const fe_type& feats, - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - for (unsigned long i = 0; i < search_rects.size(); ++i) - { - const double score = feats.compute_object_score(w, search_rects[i]); - if (score >= thresh) - { - dets.push_back(std::make_pair(score, search_rects[i])); - } - } - } - - template <typename fe_type> - typename disable_if<has_compute_object_score<fe_type> >::type compute_all_rect_scores ( - const fe_type& feats, - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - matrix<double,0,1> psi(w.size()); - psi = 0; - double prev_dot = 0; - for (unsigned long i = 0; i < search_rects.size(); ++i) - { - // Reset these back to zero every so often to avoid the accumulation of - // rounding error. Note that the only reason we do this loop in this - // complex way is to avoid needing to zero the psi vector every iteration. - if ((i%500) == 499) - { - psi = 0; - prev_dot = 0; - } - - feats.get_feature_vector(search_rects[i], psi); - const double cur_dot = dot(psi, w); - const double score = cur_dot - prev_dot; - if (score >= thresh) - { - dets.push_back(std::make_pair(score, search_rects[i])); - } - prev_dot = cur_dot; - } - } - - - feature_extractor_type feats; - std::vector<rectangle> search_rects; - bool loaded_with_image; - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void serialize ( - const scan_image_custom<T>& item, - std::ostream& out - ) - { - int version = 1; - serialize(version, out); - serialize(item.feats, out); - serialize(item.search_rects, out); - serialize(item.loaded_with_image, out); - serialize(item.get_num_dimensions(), out); - } - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void deserialize ( - scan_image_custom<T>& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version != 1) - throw serialization_error("Unsupported version found when deserializing a scan_image_custom object."); - - deserialize(item.feats, in); - deserialize(item.search_rects, in); - deserialize(item.loaded_with_image, in); - - // When developing some feature extractor, it's easy to accidentally change its - // number of dimensions and then try to deserialize data from an older version of - // your extractor into the current code. This check is here to catch that kind of - // user error. - long dims; - deserialize(dims, in); - if (item.get_num_dimensions() != dims) - throw serialization_error("Number of dimensions in serialized scan_image_custom doesn't match the expected number."); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// scan_image_custom member functions -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - scan_image_custom<Feature_extractor_type>:: - scan_image_custom ( - ) : - loaded_with_image(false) - { - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - template < - typename image_type - > - void scan_image_custom<Feature_extractor_type>:: - load ( - const image_type& img - ) - { - feats.load(img, search_rects); - loaded_with_image = true; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - bool scan_image_custom<Feature_extractor_type>:: - is_loaded_with_image ( - ) const - { - return loaded_with_image; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - void scan_image_custom<Feature_extractor_type>:: - copy_configuration( - const feature_extractor_type& fe - ) - { - feats.copy_configuration(fe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - void scan_image_custom<Feature_extractor_type>:: - copy_configuration ( - const scan_image_custom& item - ) - { - feats.copy_configuration(item.feats); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - long scan_image_custom<Feature_extractor_type>:: - get_num_dimensions ( - ) const - { - return feats.get_num_dimensions(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - void scan_image_custom<Feature_extractor_type>:: - detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - w.size() >= get_num_dimensions(), - "\t void scan_image_custom::detect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t w.size(): " << w.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - dets.clear(); - compute_all_rect_scores(feats, w,dets,thresh); - std::sort(dets.rbegin(), dets.rend(), compare_pair_rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - const rectangle scan_image_custom<Feature_extractor_type>:: - get_best_matching_rect ( - const rectangle& rect - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image(), - "\t const rectangle scan_image_custom::get_best_matching_rect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t this: " << this - ); - - - double best_score = -1; - rectangle best_rect; - for (unsigned long i = 0; i < search_rects.size(); ++i) - { - const double score = (rect.intersect(search_rects[i])).area()/(double)(rect+search_rects[i]).area(); - if (score > best_score) - { - best_score = score; - best_rect = search_rects[i]; - } - } - return best_rect; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - full_object_detection scan_image_custom<Feature_extractor_type>:: - get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& /*w*/ - ) const - { - return full_object_detection(rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - void scan_image_custom<Feature_extractor_type>:: - get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(is_loaded_with_image() && - psi.size() >= get_num_dimensions() && - obj.num_parts() == 0, - "\t void scan_image_custom::get_feature_vector()" - << "\n\t Invalid inputs were given to this function " - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t psi.size(): " << psi.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t obj.num_parts(): " << obj.num_parts() - << "\n\t this: " << this - ); - - - feats.get_feature_vector(get_best_matching_rect(obj.get_rect()), psi); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMAGE_CuSTOM_Hh_ - diff --git a/ml/dlib/dlib/image_processing/scan_image_custom_abstract.h b/ml/dlib/dlib/image_processing/scan_image_custom_abstract.h deleted file mode 100644 index ca3ba402a..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_custom_abstract.h +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright (C) 2013 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_IMAGE_CuSTOM_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_IMAGE_CuSTOM_ABSTRACT_Hh_ - -#include <vector> -#include "../matrix.h" -#include "../geometry.h" -#include "../image_processing/full_object_detection_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class example_feature_extractor - { - /*! - WHAT THIS OBJECT REPRESENTS - This object defines the interface a feature extractor must implement if it - is to be used with the scan_image_custom object defined at the bottom of - this file. - - In this case, the purpose of a feature extractor is to associated a - complete feature vector with each rectangle in an image. In particular, - each rectangle is scored by taking the dot product between this feature - vector and a weight vector. If this score is greater than a threshold then - the rectangle is output as a detection. - !*/ - - public: - - template < - typename image_type - > - void load ( - const image_type& image, - std::vector<rectangle>& candidate_objects - ); - /*! - ensures - - Loads the given image into this feature extractor. This means that - subsequent calls to get_feature_vector() will return the feature vector - corresponding to locations in the image given to load(). - - #candidate_objects == a set of bounding boxes in the given image that - might contain objects of interest. These are the locations that will be - checked for the presents of objects when this feature extractor is used - with the scan_image_custom object. - - !*/ - - void copy_configuration ( - const feature_extractor& item - ); - /*! - ensures - - Copies all the state information of item into *this, except for state - information populated by load(). More precisely, given two - feature extractor objects S1 and S2, the following sequence of - instructions should always result in both of them having the exact same - state: - S2.copy_configuration(S1); - S1.load(img, temp); - S2.load(img, temp); - !*/ - - unsigned long get_num_dimensions ( - ) const; - /*! - ensures - - returns the dimensionality of the feature vectors output by this object. - !*/ - - void get_feature_vector ( - const rectangle& obj, - matrix<double,0,1>& psi - ) const; - /*! - requires - - psi.size() >= get_num_dimensions() - (i.e. psi must have preallocated its memory before this function is called) - ensures - - This function computes the feature vector associated with the given rectangle - in obj. This rectangle is interpreted as a bounding box within the last image - given to this->load() and a feature vector describing that bounding box is - output into psi. - - The feature vector is added into psi. That is, it does not overwrite the - previous contents of psi, but instead, it adds the vector to psi. - - The dimensionality of the vector added to psi is get_num_dimensions(). This - means that elements of psi after psi(get_num_dimensions()-1) are not modified. - - #psi.size() == psi.size() - (i.e. this function does not change the size of the psi vector) - !*/ - - double compute_object_score ( - const matrix<double,0,1>& w, - const rectangle& obj - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - ensures - - This function returns the dot product between the feature vector for - object box obj and the given w vector. That is, this function computes - the same number as the following code snippet: - matrix<double,0,1> psi(w.size()); - psi = 0; - get_feature_vector(obj, psi); - return dot(psi, w); - The point of the compute_object_score() routine is to compute this dot - product in a much more efficient way than directly calling - get_feature_vector() and dot(). Therefore, compute_object_score() is an - optional function. If you can't think of a faster way to compute these - scores then do not implement compute_object_score() and the - scan_image_custom object will simply compute these scores for you. - However, it is often the case that there is something clever you can do - to make this computation faster. If that is the case, then you can - provide an implementation of this function with your feature extractor - and then scan_image_custom will use it instead of using the default - calculation method shown in the above code snippet. - !*/ - - }; - -// ---------------------------------------------------------------------------------------- - - void serialize( - const feature_extractor& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - void deserialize( - feature_extractor& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Feature_extractor_type - > - class scan_image_custom : noncopyable - { - /*! - REQUIREMENTS ON Feature_extractor_type - - must be an object with an interface compatible with the - example_feature_extractor defined at the top of this file. - - INITIAL VALUE - - is_loaded_with_image() == false - - WHAT THIS OBJECT REPRESENTS - This object is a tool for running a classifier over an image with the goal - of localizing each object present. The localization is in the form of the - bounding box around each object of interest. - - Unlike the scan_image_pyramid and scan_image_boxes objects, this image - scanner delegates all the work of constructing the object feature vector to - its Feature_extractor_type template argument. That is, scan_image_custom - simply asks the supplied feature extractor what boxes in the image we - should investigate and then asks the feature extractor for the complete - feature vector for each box. That is, scan_image_custom does not apply any - kind of pyramiding or other higher level processing to the features coming - out of the feature extractor. That means that when you use - scan_image_custom it is completely up to you to define the feature vector - used with each image box. - - THREAD SAFETY - Concurrent access to an instance of this object is not safe and should be - protected by a mutex lock except for the case where you are copying the - configuration (via copy_configuration()) of a scan_image_custom object to - many other threads. In this case, it is safe to copy the configuration of - a shared object so long as no other operations are performed on it. - !*/ - - public: - - typedef matrix<double,0,1> feature_vector_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_image_custom ( - ); - /*! - ensures - - this object is properly initialized - !*/ - - template < - typename image_type - > - void load ( - const image_type& img - ); - /*! - requires - - image_type must be a type with the following properties: - - image_type objects can be loaded into Feature_extractor_type - objects via Feature_extractor_type::load(). - ensures - - #is_loaded_with_image() == true - - Calls get_feature_extractor().load() on the given image. That is, we - will have loaded the image into the feature extractor in this - scan_image_custom object. We will also have stored the candidate - object locations generated by the feature extractor and will scan - over them when this->detect() is called. - - This object is ready to run a classifier over img to detect object - locations. Call detect() to do this. - !*/ - - bool is_loaded_with_image ( - ) const; - /*! - ensures - - returns true if this object has been loaded with an image to process and - false otherwise. - !*/ - - const feature_extractor_type& get_feature_extractor ( - ) const; - /*! - ensures - - returns a const reference to the feature_extractor_type object used - internally for local feature extraction. - !*/ - - void copy_configuration( - const feature_extractor_type& fe - ); - /*! - ensures - - This function performs the equivalent of - get_feature_extractor().copy_configuration(fe) (i.e. this function allows - you to configure the parameters of the underlying feature extractor used - by a scan_image_custom object) - !*/ - - void copy_configuration ( - const scan_image_custom& item - ); - /*! - ensures - - Copies all the state information of item into *this, except for state - information populated by load(). More precisely, given two - scan_image_custom objects S1 and S2, the following sequence of - instructions should always result in both of them having the exact same - state: - S2.copy_configuration(S1); - S1.load(img); - S2.load(img); - !*/ - - long get_num_dimensions ( - ) const; - /*! - ensures - - returns the number of dimensions in the feature vector for a candidate - object location. That is, this function returns get_feature_extractor().get_num_dimensions(). - !*/ - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - - is_loaded_with_image() == true - ensures - - Scans over all the candidate object locations produced by the feature - extractor during image loading and stores all detections into #dets. - - for all valid i: - - #dets[i].second == The candidate object location which produced this - detection. This rectangle gives the location of the detection. - - #dets[i].first == The score for this detection. This value is equal - to dot(w, feature vector for this candidate object location). - - #dets[i].first >= thresh - - #dets will be sorted in descending order. - (i.e. #dets[i].first >= #dets[j].first for all i, and j>i) - - Elements of w beyond index get_num_dimensions()-1 are ignored. I.e. only - the first get_num_dimensions() are used. - - Note that no form of non-max suppression is performed. If a locations - has a score >= thresh then it is reported in #dets. - !*/ - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - /*! - requires - - obj.num_parts() == 0 - - is_loaded_with_image() == true - - psi.size() >= get_num_dimensions() - (i.e. psi must have preallocated its memory before this function is called) - ensures - - This function allows you to determine the feature vector used for a - candidate object location output from detect(). Note that this vector is - added to psi. Note also that you must use get_full_object_detection() to - convert a rectangle from detect() into the needed full_object_detection. - - The dimensionality of the vector added to psi is get_num_dimensions(). This - means that elements of psi after psi(get_num_dimensions()-1) are not modified. - - Since scan_image_custom only searches a limited set of object locations, - not all possible rectangles can be output by detect(). So in the case - where obj.get_rect() could not arise from a call to detect(), this - function will map obj.get_rect() to the nearest possible rectangle and - then add the feature vector for the mapped rectangle into #psi. - - get_best_matching_rect(obj.get_rect()) == the rectangle obj.get_rect() - gets mapped to for feature extraction. - !*/ - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - /*! - ensures - - returns full_object_detection(rect) - (This function is here only for compatibility with the scan_image_pyramid - object) - !*/ - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - /*! - requires - - is_loaded_with_image() == true - ensures - - Since scan_image_custom only searches a limited set of object locations, - not all possible rectangles can be represented. Therefore, this function - allows you to supply a rectangle and obtain the nearest possible - candidate object location rectangle. - !*/ - - unsigned long get_num_detection_templates ( - ) const { return 1; } - /*! - ensures - - returns 1. Note that this function is here only for compatibility with - the scan_image_pyramid object. Notionally, its return value indicates - that a scan_image_custom object is always ready to detect objects once an - image has been loaded. - !*/ - - unsigned long get_num_movable_components_per_detection_template ( - ) const { return 0; } - /*! - ensures - - returns 0. Note that this function is here only for compatibility with - the scan_image_pyramid object. Its return value means that this object - does not support using movable part models. - !*/ - - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T> - void serialize ( - const scan_image_custom<T>& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - template <typename T> - void deserialize ( - scan_image_custom<T>& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMAGE_CuSTOM_ABSTRACT_Hh_ - diff --git a/ml/dlib/dlib/image_processing/scan_image_pyramid.h b/ml/dlib/dlib/image_processing/scan_image_pyramid.h deleted file mode 100644 index 455f1a649..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_pyramid.h +++ /dev/null @@ -1,1101 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_IMaGE_PYRAMID_Hh_ -#define DLIB_SCAN_IMaGE_PYRAMID_Hh_ - -#include "scan_image_pyramid_abstract.h" -#include "../matrix.h" -#include "../geometry.h" -#include "scan_image.h" -#include "../array2d.h" -#include <vector> -#include "full_object_detection.h" -#include "../image_processing/generic_image.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - class scan_image_pyramid : noncopyable - { - - public: - - typedef matrix<double,0,1> feature_vector_type; - - typedef Pyramid_type pyramid_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_image_pyramid ( - ); - - template < - typename image_type - > - void load ( - const image_type& img - ); - - inline bool is_loaded_with_image ( - ) const; - - inline void copy_configuration( - const feature_extractor_type& fe - ); - - inline void copy_configuration ( - const scan_image_pyramid& item - ); - - const Feature_extractor_type& get_feature_extractor ( - ) const { return feats_config; } - - void add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions, - const std::vector<rectangle>& movable_feature_extraction_regions - ); - - void add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions - ); - - inline unsigned long get_num_detection_templates ( - ) const; - - inline unsigned long get_num_movable_components_per_detection_template ( - ) const; - - inline unsigned long get_num_stationary_components_per_detection_template ( - ) const; - - inline unsigned long get_num_components_per_detection_template ( - ) const; - - inline long get_num_dimensions ( - ) const; - - unsigned long get_max_pyramid_levels ( - ) const; - - void set_max_pyramid_levels ( - unsigned long max_levels - ); - - inline unsigned long get_max_detections_per_template ( - ) const; - - void set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ); - - inline unsigned long get_min_pyramid_layer_width ( - ) const; - - inline unsigned long get_min_pyramid_layer_height ( - ) const; - - void set_max_detections_per_template ( - unsigned long max_dets - ); - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - - template <typename T, typename U> - friend void serialize ( - const scan_image_pyramid<T,U>& item, - std::ostream& out - ); - - template <typename T, typename U> - friend void deserialize ( - scan_image_pyramid<T,U>& item, - std::istream& in - ); - - private: - static bool compare_pair_rect ( - const std::pair<double, rectangle>& a, - const std::pair<double, rectangle>& b - ) - { - return a.first < b.first; - } - - struct detection_template - { - rectangle object_box; // always centered at (0,0) - std::vector<rectangle> rects; // template with respect to (0,0) - std::vector<rectangle> movable_rects; - }; - - friend void serialize(const detection_template& item, std::ostream& out) - { - int version = 1; - serialize(version, out); - serialize(item.object_box, out); - serialize(item.rects, out); - serialize(item.movable_rects, out); - } - friend void deserialize(detection_template& item, std::istream& in) - { - int version = 0; - deserialize(version, in); - if (version != 1) - throw serialization_error("Unexpected version found while deserializing a dlib::scan_image_pyramid::detection_template object."); - - deserialize(item.object_box, in); - deserialize(item.rects, in); - deserialize(item.movable_rects, in); - } - - void get_mapped_rect_and_metadata ( - const unsigned long number_pyramid_levels, - rectangle rect, - rectangle& mapped_rect, - detection_template& best_template, - rectangle& object_box, - unsigned long& best_level, - unsigned long& detection_template_idx - ) const; - - double get_match_score ( - rectangle r1, - rectangle r2 - ) const - { - // make the rectangles overlap as much as possible before computing the match score. - r1 = move_rect(r1, r2.tl_corner()); - return (r1.intersect(r2).area())/(double)(r1 + r2).area(); - } - - void test_coordinate_transforms() - { - for (long x = -10; x <= 10; x += 10) - { - for (long y = -10; y <= 10; y += 10) - { - const rectangle rect = centered_rect(x,y,5,6); - rectangle a; - - a = feats_config.image_to_feat_space(rect); - if (a.width() > 10000000 || a.height() > 10000000 ) - { - DLIB_CASSERT(false, "The image_to_feat_space() routine is outputting rectangles of an implausibly " - << "\nlarge size. This means there is probably a bug in your feature extractor."); - } - a = feats_config.feat_to_image_space(rect); - if (a.width() > 10000000 || a.height() > 10000000 ) - { - DLIB_CASSERT(false, "The feat_to_image_space() routine is outputting rectangles of an implausibly " - << "\nlarge size. This means there is probably a bug in your feature extractor."); - } - } - } - - } - - feature_extractor_type feats_config; // just here to hold configuration. use it to populate the feats elements. - array<feature_extractor_type> feats; - std::vector<detection_template> det_templates; - unsigned long max_dets_per_template; - unsigned long max_pyramid_levels; - unsigned long min_pyramid_layer_width; - unsigned long min_pyramid_layer_height; - - }; - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void serialize ( - const scan_image_pyramid<T,U>& item, - std::ostream& out - ) - { - int version = 3; - serialize(version, out); - serialize(item.feats_config, out); - serialize(item.feats, out); - serialize(item.det_templates, out); - serialize(item.max_dets_per_template, out); - serialize(item.max_pyramid_levels, out); - serialize(item.min_pyramid_layer_width, out); - serialize(item.min_pyramid_layer_height, out); - serialize(item.get_num_dimensions(), out); - } - -// ---------------------------------------------------------------------------------------- - - template <typename T, typename U> - void deserialize ( - scan_image_pyramid<T,U>& item, - std::istream& in - ) - { - int version = 0; - deserialize(version, in); - if (version != 3) - throw serialization_error("Unsupported version found when deserializing a scan_image_pyramid object."); - - deserialize(item.feats_config, in); - deserialize(item.feats, in); - deserialize(item.det_templates, in); - deserialize(item.max_dets_per_template, in); - deserialize(item.max_pyramid_levels, in); - deserialize(item.min_pyramid_layer_width, in); - deserialize(item.min_pyramid_layer_height, in); - - // When developing some feature extractor, it's easy to accidentally change its - // number of dimensions and then try to deserialize data from an older version of - // your extractor into the current code. This check is here to catch that kind of - // user error. - long dims; - deserialize(dims, in); - if (item.get_num_dimensions() != dims) - throw serialization_error("Number of dimensions in serialized scan_image_pyramid doesn't match the expected number."); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// scan_image_pyramid member functions -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - scan_image_pyramid ( - ) : - max_dets_per_template(10000), - max_pyramid_levels(1000), - min_pyramid_layer_width(20), - min_pyramid_layer_height(20) - { - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - template < - typename image_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - load ( - const image_type& img - ) - { - unsigned long levels = 0; - rectangle rect = get_rect(img); - - // figure out how many pyramid levels we should be using based on the image size - pyramid_type pyr; - do - { - rect = pyr.rect_down(rect); - ++levels; - } while (rect.width() >= min_pyramid_layer_width && rect.height() >= min_pyramid_layer_height && - levels < max_pyramid_levels); - - if (feats.max_size() < levels) - feats.set_max_size(levels); - feats.set_size(levels); - - for (unsigned long i = 0; i < feats.size(); ++i) - feats[i].copy_configuration(feats_config); - - // build our feature pyramid - feats[0].load(img); - if (feats.size() > 1) - { - image_type temp1, temp2; - pyr(img, temp1); - feats[1].load(temp1); - swap(temp1,temp2); - - for (unsigned long i = 2; i < feats.size(); ++i) - { - pyr(temp2, temp1); - feats[i].load(temp1); - swap(temp1,temp2); - } - } - - - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_max_detections_per_template ( - ) const - { - return max_dets_per_template; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - set_max_detections_per_template ( - unsigned long max_dets - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(max_dets > 0 , - "\t void scan_image_pyramid::set_max_detections_per_template()" - << "\n\t The max number of possible detections can't be zero. " - << "\n\t max_dets: " << max_dets - << "\n\t this: " << this - ); - - max_dets_per_template = max_dets; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - bool scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - is_loaded_with_image ( - ) const - { - return feats.size() != 0; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - copy_configuration( - const feature_extractor_type& fe - ) - { - test_coordinate_transforms(); - feats_config.copy_configuration(fe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - copy_configuration ( - const scan_image_pyramid& item - ) - { - feats_config.copy_configuration(item.feats_config); - det_templates = item.det_templates; - max_dets_per_template = item.max_dets_per_template; - max_pyramid_levels = item.max_pyramid_levels; - min_pyramid_layer_width = item.min_pyramid_layer_width; - min_pyramid_layer_height = item.min_pyramid_layer_height; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions, - const std::vector<rectangle>& movable_feature_extraction_regions - ) - { -#ifdef ENABLE_ASSERTS - // make sure requires clause is not broken - DLIB_ASSERT((get_num_detection_templates() == 0 || - (get_num_stationary_components_per_detection_template() == stationary_feature_extraction_regions.size() && - get_num_movable_components_per_detection_template() == movable_feature_extraction_regions.size())) && - center(object_box) == point(0,0), - "\t void scan_image_pyramid::add_detection_template()" - << "\n\t The number of rects in this new detection template doesn't match " - << "\n\t the number in previous detection templates." - << "\n\t get_num_stationary_components_per_detection_template(): " << get_num_stationary_components_per_detection_template() - << "\n\t stationary_feature_extraction_regions.size(): " << stationary_feature_extraction_regions.size() - << "\n\t get_num_movable_components_per_detection_template(): " << get_num_movable_components_per_detection_template() - << "\n\t movable_feature_extraction_regions.size(): " << movable_feature_extraction_regions.size() - << "\n\t this: " << this - ); - - for (unsigned long i = 0; i < movable_feature_extraction_regions.size(); ++i) - { - DLIB_ASSERT(center(movable_feature_extraction_regions[i]) == point(0,0), - "Invalid inputs were given to this function." - << "\n\t center(movable_feature_extraction_regions["<<i<<"]): " << center(movable_feature_extraction_regions[i]) - << "\n\t this: " << this - ); - } -#endif - - detection_template temp; - temp.object_box = object_box; - temp.rects = stationary_feature_extraction_regions; - temp.movable_rects = movable_feature_extraction_regions; - det_templates.push_back(temp); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions - ) - { - // an empty set of movable feature regions - const std::vector<rectangle> movable_feature_extraction_regions; - add_detection_template(object_box, stationary_feature_extraction_regions, - movable_feature_extraction_regions); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_num_detection_templates ( - ) const - { - return det_templates.size(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_num_stationary_components_per_detection_template ( - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 , - "\t unsigned long scan_image_pyramid::get_num_stationary_components_per_detection_template()" - << "\n\t You need to give some detection templates before calling this function. " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t this: " << this - ); - - return det_templates[0].rects.size(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_num_movable_components_per_detection_template ( - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 , - "\t unsigned long scan_image_pyramid::get_num_movable_components_per_detection_template()" - << "\n\t You need to give some detection templates before calling this function. " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t this: " << this - ); - - return det_templates[0].movable_rects.size(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_num_components_per_detection_template ( - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 , - "\t unsigned long scan_image_pyramid::get_num_components_per_detection_template()" - << "\n\t You need to give some detection templates before calling this function. " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t this: " << this - ); - - return get_num_movable_components_per_detection_template() + - get_num_stationary_components_per_detection_template(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_num_dimensions ( - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 , - "\t long scan_image_pyramid::get_num_dimensions()" - << "\n\t You need to give some detection templates before calling this function. " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t this: " << this - ); - - return feats_config.get_num_dimensions()*get_num_components_per_detection_template() + get_num_detection_templates(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_max_pyramid_levels ( - ) const - { - return max_pyramid_levels; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - set_max_pyramid_levels ( - unsigned long max_levels - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(max_levels > 0 , - "\t void scan_image_pyramid::set_max_pyramid_levels()" - << "\n\t You can't have zero levels. " - << "\n\t max_levels: " << max_levels - << "\n\t this: " << this - ); - - max_pyramid_levels = max_levels; - - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 && - is_loaded_with_image() && - w.size() >= get_num_dimensions(), - "\t void scan_image_pyramid::detect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t w.size(): " << w.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t this: " << this - ); - - dets.clear(); - - array<array2d<double> > saliency_images; - saliency_images.set_max_size(get_num_components_per_detection_template()); - saliency_images.set_size(get_num_components_per_detection_template()); - std::vector<std::pair<unsigned int,rectangle> > stationary_region_rects(get_num_stationary_components_per_detection_template()); - std::vector<std::pair<unsigned int,rectangle> > movable_region_rects(get_num_movable_components_per_detection_template()); - pyramid_type pyr; - std::vector<std::pair<double, point> > point_dets; - - // for all pyramid levels - for (unsigned long l = 0; l < feats.size(); ++l) - { - for (unsigned long i = 0; i < saliency_images.size(); ++i) - { - saliency_images[i].set_size(feats[l].nr(), feats[l].nc()); - const unsigned long offset = get_num_detection_templates() + feats_config.get_num_dimensions()*i; - - // build saliency images for pyramid level l - for (long r = 0; r < feats[l].nr(); ++r) - { - for (long c = 0; c < feats[l].nc(); ++c) - { - const typename feature_extractor_type::descriptor_type& descriptor = feats[l](r,c); - - double sum = 0; - for (unsigned long k = 0; k < descriptor.size(); ++k) - { - sum += w(descriptor[k].first + offset)*descriptor[k].second; - } - saliency_images[i][r][c] = sum; - } - } - } - - // now search the saliency images - for (unsigned long i = 0; i < det_templates.size(); ++i) - { - const point offset = -feats[l].image_to_feat_space(point(0,0)); - for (unsigned long j = 0; j < stationary_region_rects.size(); ++j) - { - stationary_region_rects[j] = std::make_pair(j, translate_rect(feats[l].image_to_feat_space(det_templates[i].rects[j]),offset)); - } - for (unsigned long j = 0; j < movable_region_rects.size(); ++j) - { - // Scale the size of the movable rectangle but make sure its center - // stays at point(0,0). - const rectangle temp = feats[l].image_to_feat_space(det_templates[i].movable_rects[j]); - movable_region_rects[j] = std::make_pair(j+stationary_region_rects.size(), - centered_rect(point(0,0),temp.width(), temp.height())); - } - - // Scale the object box into the feature extraction image, but keeping it - // centered at point(0,0). - rectangle scaled_object_box = feats[l].image_to_feat_space(det_templates[i].object_box); - scaled_object_box = centered_rect(point(0,0),scaled_object_box.width(), scaled_object_box.height()); - - // Each detection template gets its own special threshold in addition to - // the global detection threshold. This allows us to model the fact that - // some detection templates might be more prone to false alarming or since - // their size is different naturally require a larger or smaller threshold - // (since they integrate over a larger or smaller region of the image). - const double template_specific_thresh = w(i); - - scan_image_movable_parts(point_dets, saliency_images, scaled_object_box, - stationary_region_rects, movable_region_rects, - thresh+template_specific_thresh, max_dets_per_template); - - // convert all the point detections into rectangles at the original image scale and coordinate system - for (unsigned long j = 0; j < point_dets.size(); ++j) - { - const double score = point_dets[j].first-template_specific_thresh; - point p = point_dets[j].second; - p = feats[l].feat_to_image_space(p); - rectangle rect = translate_rect(det_templates[i].object_box, p); - rect = pyr.rect_up(rect, l); - - dets.push_back(std::make_pair(score, rect)); - } - } - } - - std::sort(dets.rbegin(), dets.rend(), compare_pair_rect); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - const rectangle scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_best_matching_rect ( - const rectangle& rect - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 , - "\t const rectangle scan_image_pyramid::get_best_matching_rect()" - << "\n\t Invalid inputs were given to this function " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t this: " << this - ); - - rectangle mapped_rect, object_box; - detection_template best_template; - unsigned long best_level, junk; - get_mapped_rect_and_metadata(max_pyramid_levels, rect, mapped_rect, best_template, object_box, best_level, junk); - return mapped_rect; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_mapped_rect_and_metadata ( - const unsigned long number_pyramid_levels, - rectangle rect, - rectangle& mapped_rect, - detection_template& best_template, - rectangle& object_box, - unsigned long& best_level, - unsigned long& detection_template_idx - ) const - { - pyramid_type pyr; - // Figure out the pyramid level which best matches rect against one of our - // detection template object boxes. - best_level = 0; - double best_match_score = -1; - - - // Find the best matching detection template for rect - for (unsigned long l = 0; l < number_pyramid_levels; ++l) - { - const rectangle temp = pyr.rect_down(rect,l); - if (temp.area() <= 1) - break; - - // At this pyramid level, what matches best? - for (unsigned long t = 0; t < det_templates.size(); ++t) - { - const double match_score = get_match_score(det_templates[t].object_box, temp); - if (match_score > best_match_score) - { - best_match_score = match_score; - best_level = l; - best_template = det_templates[t]; - detection_template_idx = t; - } - } - } - - - // Now we translate best_template into the right spot (it should be centered at the location - // determined by rect) and convert it into the feature image coordinate system. - rect = pyr.rect_down(rect,best_level); - const point offset = -feats_config.image_to_feat_space(point(0,0)); - const point origin = feats_config.image_to_feat_space(center(rect)) + offset; - for (unsigned long k = 0; k < best_template.rects.size(); ++k) - { - rectangle temp = best_template.rects[k]; - temp = feats_config.image_to_feat_space(temp); - temp = translate_rect(temp, origin); - best_template.rects[k] = temp; - } - for (unsigned long k = 0; k < best_template.movable_rects.size(); ++k) - { - rectangle temp = best_template.movable_rects[k]; - temp = feats_config.image_to_feat_space(temp); - temp = centered_rect(point(0,0), temp.width(), temp.height()); - best_template.movable_rects[k] = temp; - } - - const rectangle scaled_object_box = feats_config.image_to_feat_space(best_template.object_box); - object_box = centered_rect(origin-offset, scaled_object_box.width(), scaled_object_box.height()); - - // The input rectangle was mapped to one of the detection templates. Reverse the process - // to figure out what the mapped rectangle is in the original input space. - mapped_rect = translate_rect(best_template.object_box, feats_config.feat_to_image_space(origin-offset)); - mapped_rect = pyr.rect_up(mapped_rect, best_level); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - full_object_detection scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const - { - // fill in movable part positions. - - rectangle mapped_rect; - detection_template best_template; - unsigned long best_level, junk; - rectangle object_box; - get_mapped_rect_and_metadata(feats.size(), rect, mapped_rect, best_template, object_box, best_level, junk); - - Pyramid_type pyr; - - array2d<double> saliency_image, sum_img; - - double total_temp_score = 0; - // convert into feature space. - object_box = object_box.intersect(get_rect(feats[best_level])); - - std::vector<point> movable_parts; - movable_parts.reserve(get_num_movable_components_per_detection_template()); - for (unsigned long i = 0; i < get_num_movable_components_per_detection_template(); ++i) - { - // make the saliency_image for the ith movable part. - - const rectangle part_rect = best_template.movable_rects[i]; - const rectangle area = grow_rect(object_box, - part_rect.width()/2, - part_rect.height()/2).intersect(get_rect(feats[best_level])); - - saliency_image.set_size(area.height(), area.width()); - const unsigned long offset = get_num_detection_templates() + feats_config.get_num_dimensions()*(i+get_num_stationary_components_per_detection_template()); - - // build saliency image for pyramid level best_level - for (long r = area.top(); r <= area.bottom(); ++r) - { - for (long c = area.left(); c <= area.right(); ++c) - { - const typename feature_extractor_type::descriptor_type& descriptor = feats[best_level](r,c); - - double sum = 0; - for (unsigned long k = 0; k < descriptor.size(); ++k) - { - sum += w(descriptor[k].first + offset)*descriptor[k].second; - } - saliency_image[r-area.top()][c-area.left()] = sum; - } - } - - sum_img.set_size(saliency_image.nr(), saliency_image.nc()); - sum_filter_assign(saliency_image, sum_img, part_rect); - // Figure out where the maximizer is in sum_img. Note that we - // only look in the part of sum_img that corresponds to a location inside - // object_box. - rectangle valid_area = get_rect(sum_img); - valid_area.left() += object_box.left() - area.left(); - valid_area.top() += object_box.top() - area.top(); - valid_area.right() += object_box.right() - area.right(); - valid_area.bottom() += object_box.bottom() - area.bottom(); - double max_val = 0; - point max_loc; - for (long r = valid_area.top(); r <= valid_area.bottom(); ++r) - { - for (long c = valid_area.left(); c <= valid_area.right(); ++c) - { - if (sum_img[r][c] > max_val) - { - //if (object_box.contains(point(c,r) + area.tl_corner())) - { - max_loc = point(c,r); - max_val = sum_img[r][c]; - } - } - } - } - - if (max_val <= 0) - { - max_loc = OBJECT_PART_NOT_PRESENT; - } - else - { - total_temp_score += max_val; - // convert max_loc back into feature image space from our cropped image. - max_loc += area.tl_corner(); - - // now convert from feature space to image space. - max_loc = feats[best_level].feat_to_image_space(max_loc); - max_loc = pyr.point_up(max_loc, best_level); - max_loc = nearest_point(rect, max_loc); - } - - movable_parts.push_back(max_loc); - } - - return full_object_detection(rect, movable_parts); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const - { - // make sure requires clause is not broken - DLIB_ASSERT(get_num_detection_templates() > 0 && - is_loaded_with_image() && - psi.size() >= get_num_dimensions() && - obj.num_parts() == get_num_movable_components_per_detection_template(), - "\t void scan_image_pyramid::get_feature_vector()" - << "\n\t Invalid inputs were given to this function " - << "\n\t get_num_detection_templates(): " << get_num_detection_templates() - << "\n\t is_loaded_with_image(): " << is_loaded_with_image() - << "\n\t psi.size(): " << psi.size() - << "\n\t get_num_dimensions(): " << get_num_dimensions() - << "\n\t get_num_movable_components_per_detection_template(): " << get_num_movable_components_per_detection_template() - << "\n\t obj.num_parts(): " << obj.num_parts() - << "\n\t this: " << this - ); - DLIB_ASSERT(all_parts_in_rect(obj), - "\t void scan_image_pyramid::get_feature_vector()" - << "\n\t Invalid inputs were given to this function " - << "\n\t obj.get_rect(): " << obj.get_rect() - << "\n\t this: " << this - ); - - - - rectangle mapped_rect; - detection_template best_template; - unsigned long best_level, detection_template_idx; - rectangle object_box; - get_mapped_rect_and_metadata(feats.size(), obj.get_rect(), mapped_rect, best_template, object_box, best_level, detection_template_idx); - - psi(detection_template_idx) -= 1; - - Pyramid_type pyr; - - // put the movable rects at the places indicated by obj. - std::vector<rectangle> rects = best_template.rects; - for (unsigned long i = 0; i < obj.num_parts(); ++i) - { - if (obj.part(i) != OBJECT_PART_NOT_PRESENT) - { - // map from the original image to scaled feature space. - point loc = feats[best_level].image_to_feat_space(pyr.point_down(obj.part(i), best_level)); - // Make sure the movable part always stays within the object_box. - // Otherwise it would be at a place that the detect() function can never - // look. - loc = nearest_point(object_box, loc); - rects.push_back(translate_rect(best_template.movable_rects[i], loc)); - } - else - { - // add an empty rectangle since this part wasn't observed. - rects.push_back(rectangle()); - } - } - - // pull features out of all the boxes in rects. - for (unsigned long j = 0; j < rects.size(); ++j) - { - const rectangle rect = rects[j].intersect(get_rect(feats[best_level])); - const unsigned long template_region_id = j; - const unsigned long offset = get_num_detection_templates() + feats_config.get_num_dimensions()*template_region_id; - for (long r = rect.top(); r <= rect.bottom(); ++r) - { - for (long c = rect.left(); c <= rect.right(); ++c) - { - const typename feature_extractor_type::descriptor_type& descriptor = feats[best_level](r,c); - for (unsigned long k = 0; k < descriptor.size(); ++k) - { - psi(descriptor[k].first + offset) += descriptor[k].second; - } - } - } - } - - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(width > 0 && height > 0 , - "\t void scan_image_pyramid::set_min_pyramid_layer_size()" - << "\n\t These sizes can't be zero. " - << "\n\t width: " << width - << "\n\t height: " << height - << "\n\t this: " << this - ); - - min_pyramid_layer_width = width; - min_pyramid_layer_height = height; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_min_pyramid_layer_width ( - ) const - { - return min_pyramid_layer_width; - } - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - unsigned long scan_image_pyramid<Pyramid_type,Feature_extractor_type>:: - get_min_pyramid_layer_height ( - ) const - { - return min_pyramid_layer_height; - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMaGE_PYRAMID_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/scan_image_pyramid_abstract.h b/ml/dlib/dlib/image_processing/scan_image_pyramid_abstract.h deleted file mode 100644 index e985a3f32..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_pyramid_abstract.h +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_IMaGE_PYRAMID_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_IMaGE_PYRAMID_ABSTRACT_Hh_ - -#include "../matrix.h" -#include "../geometry.h" -#include "../image_processing.h" -#include "../array2d.h" -#include <vector> -#include "full_object_detection_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - class scan_image_pyramid : noncopyable - { - /*! - REQUIREMENTS ON Pyramid_type - - must be one of the pyramid_down objects defined in - dlib/image_transforms/image_pyramid_abstract.h or an object with - a compatible interface - - REQUIREMENTS ON Feature_extractor_type - - must be an object with an interface compatible with the hashed_feature_image - object defined in dlib/image_keypoint/hashed_feature_image_abstract.h or - with the nearest_neighbor_feature_image object defined in - dlib/image_keypoint/nearest_neighbor_feature_image_abstract.h - - INITIAL VALUE - - get_num_detection_templates() == 0 - - is_loaded_with_image() == false - - get_max_detections_per_template() == 10000 - - get_max_pyramid_levels() == 1000 - - get_min_pyramid_layer_width() == 20 - - get_min_pyramid_layer_height() == 20 - - WHAT THIS OBJECT REPRESENTS - This object is a tool for running a sliding window classifier over - an image pyramid. This object can also be understood as a general - tool for implementing the spatial pyramid models described in the paper: - Beyond Bags of Features: Spatial Pyramid Matching for Recognizing - Natural Scene Categories by Svetlana Lazebnik, Cordelia Schmid, - and Jean Ponce - It also includes the ability to represent movable part models. - - - - - The sliding window classifiers used by this object have three parts: - 1. The underlying feature extraction provided by Feature_extractor_type - objects, which associate a vector with each location in an image. - - 2. A detection template. This is a rectangle which defines the shape of a - sliding window (i.e. the object_box), as well as a set of rectangular feature - extraction regions inside it. This set of regions defines the spatial - structure of the overall feature extraction within a sliding window. In - particular, each location of a sliding window has a feature vector - associated with it. This feature vector is defined as follows: - - Let N denote the number of feature extraction zones. - - Let M denote the dimensionality of the vectors output by Feature_extractor_type - objects. - - Let F(i) == the M dimensional vector which is the sum of all vectors - given by our Feature_extractor_type object inside the i-th feature extraction - zone. - - Then the feature vector for a sliding window is an M*N dimensional vector - [F(1) F(2) F(3) ... F(N)] (i.e. it is a concatenation of the N vectors). - This feature vector can be thought of as a collection of N "bags of features", - each bag coming from a spatial location determined by one of the rectangular - feature extraction zones. - - 3. A weight vector and a threshold value. The dot product between the weight - vector and the feature vector for a sliding window location gives the score - of the window. If this score is greater than the threshold value then the - window location is output as a detection. - - Finally, the sliding window classifiers described above are applied to every level of - an image pyramid. Moreover, some of the feature extraction zones are allowed to move - freely within the object box. This means that when we are sliding the classifier over - an image, some feature extraction zones are stationary (i.e. always in the same place - relative to the object box) while others are allowed to move anywhere within the object - box. In particular, the movable regions are placed at the locations that maximize the - score of the classifier. Note further that each of the movable feature extraction - zones must pass a threshold test for it to be included. That is, if the score that a - movable zone would contribute to the overall score for a sliding window location is not - positive then that zone is not included in the feature vector (i.e. its part of the - feature vector is set to zero. This way the length of the feature vector stays - constant). This movable region construction allows us to represent objects with parts - that move around relative to the object box. For example, a human has hands but they - aren't always in the same place relative to a person's bounding box. - - THREAD SAFETY - Concurrent access to an instance of this object is not safe and should be protected - by a mutex lock except for the case where you are copying the configuration - (via copy_configuration()) of a scan_image_pyramid object to many other threads. - In this case, it is safe to copy the configuration of a shared object so long - as no other operations are performed on it. - !*/ - public: - - typedef matrix<double,0,1> feature_vector_type; - - typedef Pyramid_type pyramid_type; - typedef Feature_extractor_type feature_extractor_type; - - scan_image_pyramid ( - ); - /*! - ensures - - this object is properly initialized - !*/ - - template < - typename image_type - > - void load ( - const image_type& img - ); - /*! - requires - - image_type must be a type with the following properties: - - image_type is default constructable. - - image_type is swappable by the global swap() function. - - image_type logically represents some kind of image and therefore its - number of rows and columns can be queried via num_rows(img) and - num_columns(img) respectively. - - image_type objects can be loaded into Feature_extractor_type - objects via Feature_extractor_type::load(). - - image_type objects can be used with Pyramid_type. That is, - if pyr is an object of type Pyramid_type while img1 and img2 - are objects of image_type, then pyr(img1,img2) should be - a valid expression which downsamples img1 into img2. - ensures - - #is_loaded_with_image() == true - - This object is ready to run sliding window classifiers over img. Call - detect() to do this. - !*/ - - bool is_loaded_with_image ( - ) const; - /*! - ensures - - returns true if this object has been loaded with an image to process - and false otherwise. - !*/ - - const feature_extractor_type& get_feature_extractor ( - ) const; - /*! - ensures - - returns a const reference to the feature_extractor_type object used - internally for local feature extraction. - !*/ - - void copy_configuration( - const feature_extractor_type& fe - ); - /*! - ensures - - This function performs the equivalent of - get_feature_extractor().copy_configuration(fe) (i.e. this function allows - you to configure the parameters of the underlying feature extractor used - by a scan_image_pyramid object) - !*/ - - void copy_configuration ( - const scan_image_pyramid& item - ); - /*! - ensures - - copies all the state information of item into *this, except for state - information populated by load(). More precisely, given two scan_image_pyramid - objects S1 and S2, the following sequence of instructions should always - result in both of them having the exact same state. - S2.copy_configuration(S1); - S1.load(img); - S2.load(img); - !*/ - - void add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions, - const std::vector<rectangle>& movable_feature_extraction_regions - ); - /*! - requires - - center(object_box) == point(0,0) - - for all valid i: - - center(movable_feature_extraction_regions[i]) == point(0,0) - - if (get_num_detection_templates() > 0) then - - get_num_stationary_components_per_detection_template() == stationary_feature_extraction_regions.size() - - get_num_movable_components_per_detection_template() == movable_feature_extraction_regions.size() - (i.e. if you already have detection templates in this object, then - any new detection template must declare a consistent number of - feature extraction regions) - ensures - - Adds another detection template to this object. In particular, object_box - defines the size and shape of a sliding window while stationary_feature_extraction_regions - and movable_feature_extraction_regions defines the locations for feature extraction as - discussed in the WHAT THIS OBJECT REPRESENTS section above. Note also that the locations of - the stationary feature extraction regions are relative to the object_box. - - #get_num_detection_templates() == get_num_detection_templates() + 1 - - The order of rectangles in stationary_feature_extraction_regions and - movable_feature_extraction_regions matters. Recall that each rectangle - gets its own set of features. So given two different templates, their - i-th rectangles will both share the same part of the weight vector (i.e. the w - supplied to detect()). So there should be some reasonable correspondence - between the rectangle ordering in different detection templates. For, - example, different detection templates should place corresponding feature - extraction regions in roughly the same part of the object_box. - - #get_num_stationary_components_per_detection_template() = stationary_feature_extraction_regions.size() - - #get_num_movable_components_per_detection_template() = movable_feature_extraction_regions.size() - !*/ - - void add_detection_template ( - const rectangle& object_box, - const std::vector<rectangle>& stationary_feature_extraction_regions - ); - /*! - ensures - - calls add_detection_template(object_box, stationary_feature_extraction_regions, empty_list) - where empty_list is a vector of size 0. I.e. this function is just a convenience - routine for adding detection templates with no movable regions. - !*/ - - unsigned long get_num_detection_templates ( - ) const; - /*! - ensures - - returns the number of detection templates in this object - !*/ - - unsigned long get_num_stationary_components_per_detection_template ( - ) const; - /*! - requires - - get_num_detection_templates() > 0 - ensures - - A detection template is a rectangle which defines the shape of a sliding - window (the object_box), as well as a set of rectangles which define - feature extraction zones. This function returns the number of stationary - feature extraction zones in the detection templates used by this object. - !*/ - - unsigned long get_num_movable_components_per_detection_template ( - ) const; - /*! - requires - - get_num_detection_templates() > 0 - ensures - - A detection template is a rectangle which defines the shape of a sliding - window (the object_box), as well as a set of rectangles which define - feature extraction zones. This function returns the number of movable - feature extraction zones in the detection templates used by this object. - !*/ - - unsigned long get_num_components_per_detection_template ( - ) const; - /*! - requires - - get_num_detection_templates() > 0 - ensures - - returns the total number of feature extraction zones in the detection - templates used by this object. That is, returns the following: - - get_num_movable_components_per_detection_template() + - get_num_stationary_components_per_detection_template() - !*/ - - long get_num_dimensions ( - ) const; - /*! - requires - - get_num_detection_templates() > 0 - ensures - - returns the number of dimensions in the feature vector for a sliding window - location. This value is the dimensionality of the underlying feature vectors - produced by Feature_extractor_type times (get_num_stationary_components_per_detection_template() + - get_num_movable_components_per_detection_template()). - !*/ - - unsigned long get_max_pyramid_levels ( - ) const; - /*! - ensures - - returns the maximum number of image pyramid levels this object will use. - Note that #get_max_pyramid_levels() == 1 indicates that no image pyramid - will be used at all. That is, only the original image will be processed - and no lower scale versions will be created. - !*/ - - void set_max_pyramid_levels ( - unsigned long max_levels - ); - /*! - requires - - max_levels > 0 - ensures - - #get_max_pyramid_levels() == max_levels - !*/ - - void set_min_pyramid_layer_size ( - unsigned long width, - unsigned long height - ); - /*! - requires - - width > 0 - - height > 0 - ensures - - #get_min_pyramid_layer_width() == width - - #get_min_pyramid_layer_height() == height - !*/ - - inline unsigned long get_min_pyramid_layer_width ( - ) const; - /*! - ensures - - returns the smallest allowable width of an image in the image pyramid. - All pyramids will always include the original input image, however, no - pyramid levels will be created which have a width smaller than the - value returned by this function. - !*/ - - inline unsigned long get_min_pyramid_layer_height ( - ) const; - /*! - ensures - - returns the smallest allowable height of an image in the image pyramid. - All pyramids will always include the original input image, however, no - pyramid levels will be created which have a height smaller than the - value returned by this function. - !*/ - - unsigned long get_max_detections_per_template ( - ) const; - /*! - ensures - - For each image pyramid layer and detection template, this object scans a sliding - window classifier over an image and produces a number of detections. This - function returns a number which defines a hard upper limit on the number of - detections allowed by a single scan. This means that the total number of - possible detections produced by detect() is get_max_detections_per_template()* - get_num_detection_templates()*(number of image pyramid layers). Additionally, - if the maximum number of detections is reached during a scan then this object - will return a random subsample of all detections which are above the detection - threshold. - !*/ - - void set_max_detections_per_template ( - unsigned long max_dets - ); - /*! - requires - - max_dets > 0 - ensures - - #get_max_detections_per_template() == max_dets - !*/ - - void detect ( - const feature_vector_type& w, - std::vector<std::pair<double, rectangle> >& dets, - const double thresh - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - - is_loaded_with_image() == true - - get_num_detection_templates() > 0 - ensures - - Scans all the detection templates over all pyramid layers as discussed in the - WHAT THIS OBJECT REPRESENTS section and stores all detections into #dets. - - for all valid i: - - #dets[i].second == The object box which produced this detection. This rectangle gives - the location of the detection. Note that the rectangle will have been converted back into - the original image input space. That is, if this detection was made at a low level in the - image pyramid then the object box will have been automatically mapped up the pyramid layers - to the original image space. Or in other words, if you plot #dets[i].second on top of the - image given to load() it will show up in the right place. - - #dets[i].first == The score for this detection. This value is equal to dot(w, feature vector - for this sliding window location). - - #dets[i].first >= thresh - - #dets will be sorted in descending order. (i.e. #dets[i].first >= #dets[j].first for all i, and j>i) - - Elements of w beyond index get_num_dimensions()-1 are ignored. I.e. only the first - get_num_dimensions() are used. - - Note that no form of non-max suppression is performed. If a window has a score >= thresh - then it is reported in #dets (assuming the limit imposed by get_max_detections_per_template() hasn't - been reached). - !*/ - - const rectangle get_best_matching_rect ( - const rectangle& rect - ) const; - /*! - requires - - get_num_detection_templates() > 0 - ensures - - Since scan_image_pyramid is a sliding window classifier system, not all possible rectangles - can be represented. Therefore, this function allows you to supply a rectangle and obtain the - nearest possible sliding window rectangle. - !*/ - - void get_feature_vector ( - const full_object_detection& obj, - feature_vector_type& psi - ) const; - /*! - requires - - all_parts_in_rect(obj) == true - - obj.num_parts() == get_num_movable_components_per_detection_template() - - is_loaded_with_image() == true - - get_num_detection_templates() > 0 - - psi.size() >= get_num_dimensions() - (i.e. psi must have preallocated its memory before this function is called) - ensures - - This function allows you to determine the feature vector used for a - sliding window location. Note that this vector is added to psi. Note - also that you must use get_full_object_detection() to convert a rect from - detect() into the needed full_object_detection. - - The dimensionality of the vector added to psi is get_num_dimensions(). This - means that elements of psi after psi(get_num_dimensions()-1) are not modified. - - Since scan_image_pyramid is a sliding window classifier system, not all - possible rectangles can be output by detect(). So in the case where - obj.get_rect() could not arise from a call to detect(), this function - will map obj.get_rect() to the nearest possible object box and then add - the feature vector for the mapped rectangle into #psi. - - get_best_matching_rect(obj.get_rect()) == the rectangle obj.get_rect() - gets mapped to for feature extraction. - !*/ - - full_object_detection get_full_object_detection ( - const rectangle& rect, - const feature_vector_type& w - ) const; - /*! - requires - - w.size() >= get_num_dimensions() - - is_loaded_with_image() == true - - get_num_detection_templates() > 0 - ensures - - This function allows you to determine the full_object_detection - corresponding to a sliding window location. Note that the detect() - routine doesn't return the locations of the movable parts in a detected - object. Therefore, if you are using any movable parts in your model you - must use get_full_object_detection() to find out where the movable parts - were detected. To do this, you supply the w and detected rectangle. - Then the corresponding fully populated full_object_detection will be - returned. - - returns a full_object_detection, OBJ, such that: - - OBJ.get_rect() == rect - - OBJ.num_parts() == get_num_movable_components_per_detection_template() - - OBJ.part(i) == the location of the i-th movable part inside this detection, - or OBJECT_PART_NOT_PRESENT if the part was not found. - !*/ - - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void serialize ( - const scan_image_pyramid<Pyramid_type,Feature_extractor_type>& item, - std::ostream& out - ); - /*! - provides serialization support - !*/ - - template < - typename Pyramid_type, - typename Feature_extractor_type - > - void deserialize ( - scan_image_pyramid<Pyramid_type,Feature_extractor_type>& item, - std::istream& in - ); - /*! - provides deserialization support - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMaGE_PYRAMID_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/scan_image_pyramid_tools.h b/ml/dlib/dlib/image_processing/scan_image_pyramid_tools.h deleted file mode 100644 index 874b995b4..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_pyramid_tools.h +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SCAN_IMaGE_PYRAMID_TOOLS_Hh_ -#define DLIB_SCAN_IMaGE_PYRAMID_TOOLS_Hh_ - -#include "scan_image_pyramid_tools_abstract.h" -#include "../statistics.h" -#include <list> -#include "../geometry.h" -#include <iostream> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - inline bool compare_first ( - const std::pair<unsigned long,rectangle>& a, - const std::pair<unsigned long,rectangle>& b - ) - { - return a.first < b.first; - } - } - - - template <typename image_scanner_type> - std::vector<rectangle> determine_object_boxes ( - const image_scanner_type& scanner, - const std::vector<rectangle>& rects, - double min_match_score - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < min_match_score && min_match_score <= 1, - "\t std::vector<rectangle> determine_object_boxes()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t min_match_score: " << min_match_score - ); - - typename image_scanner_type::pyramid_type pyr; - - typedef std::list<std::pair<unsigned long, rectangle> > list_type; - - unsigned long max_area = 0; - - // Copy rects into sorted_rects and sort them in order of increasing area. But - // only include the rectangles that aren't already obtainable by the scanner. - list_type sorted_rects; - for (unsigned long i = 0; i < rects.size(); ++i) - { - if (scanner.get_num_detection_templates() > 0) - { - rectangle temp = scanner.get_best_matching_rect(rects[i]); - const double match_score = (rects[i].intersect(temp).area())/(double)(rects[i] + temp).area(); - // skip this rectangle if it's already matched well enough. - if (match_score > min_match_score) - continue; - } - max_area = std::max(rects[i].area(), max_area); - sorted_rects.push_back(std::make_pair(rects[i].area(), rects[i])); - } - sorted_rects.sort(dlib::impl::compare_first); - - // Make sure this area value is comfortably larger than all the - // rectangles' areas. - max_area = 3*max_area + 100; - - std::vector<rectangle> object_boxes; - - while (sorted_rects.size() != 0) - { - rectangle cur = sorted_rects.front().second; - sorted_rects.pop_front(); - object_boxes.push_back(centered_rect(point(0,0), cur.width(), cur.height())); - - // Scale cur up the image pyramid and remove any rectangles which match. - // But also stop when cur gets large enough to not match anything. - for (unsigned long itr = 0; - itr < scanner.get_max_pyramid_levels() && cur.area() < max_area; - ++itr) - { - list_type::iterator i = sorted_rects.begin(); - while (i != sorted_rects.end()) - { - const rectangle temp = move_rect(i->second, cur.tl_corner()); - const double match_score = (cur.intersect(temp).area())/(double)(cur + temp).area(); - if (match_score > min_match_score) - { - i = sorted_rects.erase(i); - } - else - { - ++i; - } - } - - cur = pyr.rect_up(cur); - } - - } - - return object_boxes; - } - -// ---------------------------------------------------------------------------------------- - - template <typename image_scanner_type> - std::vector<rectangle> determine_object_boxes ( - const image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - double min_match_score - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < min_match_score && min_match_score <= 1, - "\t std::vector<rectangle> determine_object_boxes()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t min_match_score: " << min_match_score - ); - - std::vector<rectangle> temp; - for (unsigned long i = 0; i < rects.size(); ++i) - { - for (unsigned long j = 0; j < rects[i].size(); ++j) - { - temp.push_back(rects[i][j]); - } - } - - return determine_object_boxes(scanner, temp, min_match_score); - } - -// ---------------------------------------------------------------------------------------- - - template <typename image_scanner_type> - void setup_grid_detection_templates ( - image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - unsigned int cells_x, - unsigned int cells_y, - double min_match_score = 0.75 - ) - { - const std::vector<rectangle>& object_boxes = determine_object_boxes(scanner, rects, min_match_score); - for (unsigned long i = 0; i < object_boxes.size(); ++i) - { - scanner.add_detection_template(object_boxes[i], create_grid_detection_template(object_boxes[i], cells_x, cells_y)); - } - } - -// ---------------------------------------------------------------------------------------- - - template <typename image_scanner_type> - void setup_grid_detection_templates_verbose ( - image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - unsigned int cells_x, - unsigned int cells_y, - double min_match_score = 0.75 - ) - { - const std::vector<rectangle>& object_boxes = determine_object_boxes(scanner, rects, min_match_score); - std::cout << "number of detection templates: "<< object_boxes.size() << std::endl; - for (unsigned long i = 0; i < object_boxes.size(); ++i) - { - std::cout << " object box " << i << ": width: " << object_boxes[i].width() - << " height: "<< object_boxes[i].height() << std::endl; - scanner.add_detection_template(object_boxes[i], create_grid_detection_template(object_boxes[i], cells_x, cells_y)); - } - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMaGE_PYRAMID_TOOLS_Hh_ - diff --git a/ml/dlib/dlib/image_processing/scan_image_pyramid_tools_abstract.h b/ml/dlib/dlib/image_processing/scan_image_pyramid_tools_abstract.h deleted file mode 100644 index 83a572df7..000000000 --- a/ml/dlib/dlib/image_processing/scan_image_pyramid_tools_abstract.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SCAN_IMaGE_PYRAMID_TOOLS_ABSTRACT_Hh_ -#ifdef DLIB_SCAN_IMaGE_PYRAMID_TOOLS_ABSTRACT_Hh_ - -#include "scan_image_pyramid_abstract.h" -#include <vector> -#include "../geometry.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - std::vector<rectangle> determine_object_boxes ( - const image_scanner_type& scanner, - const std::vector<rectangle>& rects, - double min_match_score - ); - /*! - requires - - 0 < min_match_score <= 1 - - image_scanner_type == an implementation of the scan_image_pyramid - object defined in dlib/image_processing/scan_image_pyramid_tools_abstract.h - ensures - - returns a set of object boxes which, when used as detection templates with - the given scanner, can attain at least min_match_score alignment with every - element of rects. Note that the alignment between two rectangles A and B is - defined as: - (A.intersect(B).area())/(double)(A+B).area() - - Only elements of rects which are not already well matched by the scanner are - considered. That is, if the scanner already has some detection templates in - it then the contents of rects will be checked against those detection - templates and elements with a match better than min_match_score are ignore. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - std::vector<rectangle> determine_object_boxes ( - const image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - double min_match_score - ); - /*! - requires - - 0 < min_match_score <= 1 - - image_scanner_type == an implementation of the scan_image_pyramid - object defined in dlib/image_processing/scan_image_pyramid_tools_abstract.h - ensures - - copies all rectangles in rects into a std::vector<rectangle> object, call it - R. Then this function returns determine_object_boxes(scanner,R,min_match_score). - That is, it just called the version of determine_object_boxes() defined above - and returns the results. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - void setup_grid_detection_templates ( - image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - unsigned int cells_x, - unsigned int cells_y, - double min_match_score = 0.75 - ); - /*! - requires - - cells_x > 0 - - cells_y > 0 - - 0 < min_match_score <= 1 - - image_scanner_type == an implementation of the scan_image_pyramid - object defined in dlib/image_processing/scan_image_pyramid_tools_abstract.h - ensures - - uses determine_object_boxes(scanner,rects,min_match_score) to obtain a set of - object boxes and then adds them to the given scanner object as detection templates. - Also uses create_grid_detection_template(object_box, cells_x, cells_y) to create - each feature extraction region. Therefore, the detection templates will extract - features from a regular grid inside each object box. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner_type - > - void setup_grid_detection_templates_verbose ( - image_scanner_type& scanner, - const std::vector<std::vector<rectangle> >& rects, - unsigned int cells_x, - unsigned int cells_y, - double min_match_score = 0.75 - ); - /*! - requires - - cells_x > 0 - - cells_y > 0 - - 0 < min_match_score <= 1 - - image_scanner_type == an implementation of the scan_image_pyramid - object defined in dlib/image_processing/scan_image_pyramid_tools_abstract.h - ensures - - this function is identical to setup_grid_detection_templates() except - that it also outputs the selected detection templates to standard out. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SCAN_IMaGE_PYRAMID_TOOLS_ABSTRACT_Hh_ - diff --git a/ml/dlib/dlib/image_processing/setup_hashed_features.h b/ml/dlib/dlib/image_processing/setup_hashed_features.h deleted file mode 100644 index 5b82cecb4..000000000 --- a/ml/dlib/dlib/image_processing/setup_hashed_features.h +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SETUP_HAShED_FEATURES_Hh_ -#define DLIB_SETUP_HAShED_FEATURES_Hh_ - -#include "setup_hashed_features_abstract.h" -#include "scan_image_pyramid.h" -#include "scan_image_boxes.h" -#include "../lsh.h" -#include "../statistics.h" -#include "../image_keypoint.h" -#include "../geometry.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class image_hash_construction_failure : public error - { - public: - image_hash_construction_failure( - const std::string& a - ): error(a) {} - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner - > - void use_uniform_feature_weights ( - image_scanner& scanner - ) - { - typename image_scanner::feature_extractor_type fe; - fe.copy_configuration(scanner.get_feature_extractor()); - fe.use_uniform_feature_weights(); - scanner.copy_configuration(fe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner - > - void use_relative_feature_weights ( - image_scanner& scanner - ) - { - typename image_scanner::feature_extractor_type fe; - fe.copy_configuration(scanner.get_feature_extractor()); - fe.use_relative_feature_weights(); - scanner.copy_configuration(fe); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// stuff for scan_image_pyramid -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename pyramid, - typename feature_extractor, - template <typename fe, typename hash> class feature_image - > - void setup_hashed_features ( - scan_image_pyramid<pyramid, feature_image<feature_extractor, projection_hash> >& scanner, - const image_array& images, - const feature_extractor& fe, - int bits, - unsigned long num_samples = 200000 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < bits && bits <= 32 && - num_samples > 1 && - images.size() > 0, - "\t void setup_hashed_features()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t bits: " << bits - << "\n\t num_samples: " << num_samples - << "\n\t images.size(): " << images.size() - ); - - pyramid pyr; - - const random_subset_selector<typename feature_extractor::descriptor_type>& samps = - randomly_sample_image_features(images, pyr, fe, num_samples); - - if (samps.size() <= 1) - throw dlib::image_hash_construction_failure("Images too small, not able to gather enough samples to make hash"); - - projection_hash phash = create_random_projection_hash(samps, bits); - - feature_image<feature_extractor, projection_hash> hfe; - hfe.copy_configuration(scanner.get_feature_extractor()); - hfe.set_hash(phash); - hfe.copy_configuration(fe); - scanner.copy_configuration(hfe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename pyramid, - typename feature_extractor, - template <typename fe, typename hash> class feature_image - > - void setup_hashed_features ( - scan_image_pyramid<pyramid, feature_image<feature_extractor, projection_hash> >& scanner, - const image_array& images, - int bits, - unsigned long num_samples = 200000 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < bits && bits <= 32 && - num_samples > 1 && - images.size() > 0, - "\t void setup_hashed_features()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t bits: " << bits - << "\n\t num_samples: " << num_samples - << "\n\t images.size(): " << images.size() - ); - - feature_extractor fe; - setup_hashed_features(scanner, images, fe, bits, num_samples); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// stuff for scan_image_boxes -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename feature_extractor, - template <typename fe, typename hash> class feature_image, - typename box_generator - > - void setup_hashed_features ( - scan_image_boxes<feature_image<feature_extractor, projection_hash>,box_generator >& scanner, - const image_array& images, - const feature_extractor& fe, - int bits, - unsigned long num_samples = 200000 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < bits && bits <= 32 && - num_samples > 1 && - images.size() > 0, - "\t void setup_hashed_features()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t bits: " << bits - << "\n\t num_samples: " << num_samples - << "\n\t images.size(): " << images.size() - ); - - pyramid_disable pyr; - - const random_subset_selector<typename feature_extractor::descriptor_type>& samps = - randomly_sample_image_features(images, pyr, fe, num_samples); - - if (samps.size() <= 1) - throw dlib::image_hash_construction_failure("Images too small, not able to gather enough samples to make hash"); - - projection_hash phash = create_random_projection_hash(samps, bits); - - feature_image<feature_extractor, projection_hash> hfe; - hfe.copy_configuration(scanner.get_feature_extractor()); - hfe.set_hash(phash); - hfe.copy_configuration(fe); - scanner.copy_configuration(hfe); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename feature_extractor, - template <typename fe, typename hash> class feature_image, - typename box_generator - > - void setup_hashed_features ( - scan_image_boxes<feature_image<feature_extractor, projection_hash>,box_generator>& scanner, - const image_array& images, - int bits, - unsigned long num_samples = 200000 - ) - { - // make sure requires clause is not broken - DLIB_ASSERT(0 < bits && bits <= 32 && - num_samples > 1 && - images.size() > 0, - "\t void setup_hashed_features()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t bits: " << bits - << "\n\t num_samples: " << num_samples - << "\n\t images.size(): " << images.size() - ); - - feature_extractor fe; - setup_hashed_features(scanner, images, fe, bits, num_samples); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SETUP_HAShED_FEATURES_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/setup_hashed_features_abstract.h b/ml/dlib/dlib/image_processing/setup_hashed_features_abstract.h deleted file mode 100644 index 886411cd4..000000000 --- a/ml/dlib/dlib/image_processing/setup_hashed_features_abstract.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (C) 2011 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SETUP_HAShED_FEATURES_ABSTRACT_Hh_ -#ifdef DLIB_SETUP_HAShED_FEATURES_ABSTRACT_Hh_ - -#include "scan_image_pyramid_abstract.h" -#include "scan_image_boxes_abstract.h" -#include "../lsh/projection_hash_abstract.h" -#include "../image_keypoint/hashed_feature_image_abstract.h" -#include "../image_keypoint/binned_vector_feature_image_abstract.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class image_hash_construction_failure : public error - { - /*! - WHAT THIS OBJECT REPRESENTS - This is the exception object used by the routines in this file. - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner - > - void use_uniform_feature_weights ( - image_scanner& scanner - ); - /*! - requires - - image_scanner should be either scan_image_pyramid or scan_image_boxes and - should use the hashed_feature_image as its local feature extractor. - ensures - - #scanner.get_feature_extractor().uses_uniform_feature_weights() == true - (i.e. Make the scanner's feature extractor use the uniform feature weighting - scheme) - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_scanner - > - void use_relative_feature_weights ( - image_scanner& scanner - ); - /*! - requires - - image_scanner should be either scan_image_pyramid or scan_image_boxes and - should use the hashed_feature_image as its local feature extractor. - ensures - - #scanner.get_feature_extractor().uses_uniform_feature_weights() == false - (i.e. Make the scanner's feature extractor use the relative feature weighting - scheme) - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename pyramid, - typename feature_extractor - template <typename fe, typename hash> class feature_image - > - void setup_hashed_features ( - scan_image_pyramid<pyramid, feature_image<feature_extractor, projection_hash> >& scanner, - const image_array& images, - const feature_extractor& fe, - int bits, - unsigned long num_samples = 200000 - ); - /*! - requires - - 0 < bits <= 32 - - num_samples > 1 - - images.size() > 0 - - it must be valid to pass images[0] into scanner.load(). - (also, image_array must be an implementation of dlib/array/array_kernel_abstract.h) - - feature_image == must be either hashed_feature_image, binned_vector_feature_image, - or a type with a compatible interface. - ensures - - Creates a projection_hash suitable for hashing the feature vectors produced by - fe and then configures scanner to use this hash function. - - The hash function will map vectors into integers in the range [0, pow(2,bits)) - - The hash function will be setup so that it hashes a random sample of num_samples - vectors from fe such that each bin ends up with roughly the same number of - elements in it. - throws - - image_hash_construction_failure - This exception is thrown if there is a problem creating the projection_hash. - This should only happen the images are so small they contain less than 2 - feature vectors. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename pyramid, - typename feature_extractor - template <typename fe, typename hash> class feature_image - > - void setup_hashed_features ( - scan_image_pyramid<pyramid, feature_image<feature_extractor, projection_hash> >& scanner, - const image_array& images, - int bits, - unsigned long num_samples = 200000 - ); - /*! - requires - - 0 < bits <= 32 - - num_samples > 1 - - images.size() > 0 - - it must be valid to pass images[0] into scanner.load(). - (also, image_array must be an implementation of dlib/array/array_kernel_abstract.h) - - feature_image == must be either hashed_feature_image, binned_vector_feature_image, - or a type with a compatible interface. - ensures - - performs: setup_hashed_features(scanner, images, feature_extractor(), bits, num_samples) - throws - - image_hash_construction_failure - This exception is thrown if there is a problem creating the projection_hash. - This should only happen the images are so small they contain less than 2 - feature vectors. - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename feature_extractor, - template <typename fe, typename hash> class feature_image - typename box_generator - > - void setup_hashed_features ( - scan_image_boxes<feature_image<feature_extractor, projection_hash>,box_generator>& scanner, - const image_array& images, - const feature_extractor& fe, - int bits, - unsigned long num_samples = 200000 - ); - /*! - requires - - 0 < bits <= 32 - - num_samples > 1 - - images.size() > 0 - - it must be valid to pass images[0] into scanner.load(). - (also, image_array must be an implementation of dlib/array/array_kernel_abstract.h) - - feature_image == must be either hashed_feature_image, binned_vector_feature_image, - or a type with a compatible interface. - ensures - - Creates a projection_hash suitable for hashing the feature vectors produced by - fe and then configures scanner to use this hash function. - - The hash function will map vectors into integers in the range [0, pow(2,bits)) - - The hash function will be setup so that it hashes a random sample of num_samples - vectors from fe such that each bin ends up with roughly the same number of - elements in it. - throws - - image_hash_construction_failure - This exception is thrown if there is a problem creating the projection_hash. - This should only happen the images are so small they contain less than 2 - feature vectors. - !*/ - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array, - typename feature_extractor, - template <typename fe, typename hash> class feature_image - typename box_generator - > - void setup_hashed_features ( - scan_image_boxes<feature_image<feature_extractor, projection_hash>,box_generator>& scanner, - const image_array& images, - int bits, - unsigned long num_samples = 200000 - ); - /*! - requires - - 0 < bits <= 32 - - num_samples > 1 - - images.size() > 0 - - it must be valid to pass images[0] into scanner.load(). - (also, image_array must be an implementation of dlib/array/array_kernel_abstract.h) - - feature_image == must be either hashed_feature_image, binned_vector_feature_image, - or a type with a compatible interface. - ensures - - performs: setup_hashed_features(scanner, images, feature_extractor(), bits, num_samples) - throws - - image_hash_construction_failure - This exception is thrown if there is a problem creating the projection_hash. - This should only happen the images are so small they contain less than 2 - feature vectors. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SETUP_HAShED_FEATURES_ABSTRACT_Hh_ - - diff --git a/ml/dlib/dlib/image_processing/shape_predictor.h b/ml/dlib/dlib/image_processing/shape_predictor.h deleted file mode 100644 index 05e9a60fd..000000000 --- a/ml/dlib/dlib/image_processing/shape_predictor.h +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SHAPE_PREDICToR_H_ -#define DLIB_SHAPE_PREDICToR_H_ - -#include "shape_predictor_abstract.h" -#include "full_object_detection.h" -#include "../algs.h" -#include "../matrix.h" -#include "../geometry.h" -#include "../pixel.h" -#include "../statistics.h" -#include <utility> - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - namespace impl - { - struct split_feature - { - unsigned long idx1; - unsigned long idx2; - float thresh; - - friend inline void serialize (const split_feature& item, std::ostream& out) - { - dlib::serialize(item.idx1, out); - dlib::serialize(item.idx2, out); - dlib::serialize(item.thresh, out); - } - friend inline void deserialize (split_feature& item, std::istream& in) - { - dlib::deserialize(item.idx1, in); - dlib::deserialize(item.idx2, in); - dlib::deserialize(item.thresh, in); - } - }; - - - // a tree is just a std::vector<impl::split_feature>. We use this function to navigate the - // tree nodes - inline unsigned long left_child (unsigned long idx) { return 2*idx + 1; } - /*! - ensures - - returns the index of the left child of the binary tree node idx - !*/ - inline unsigned long right_child (unsigned long idx) { return 2*idx + 2; } - /*! - ensures - - returns the index of the left child of the binary tree node idx - !*/ - - struct regression_tree - { - std::vector<split_feature> splits; - std::vector<matrix<float,0,1> > leaf_values; - - unsigned long num_leaves() const { return leaf_values.size(); } - - inline const matrix<float,0,1>& operator()( - const std::vector<float>& feature_pixel_values, - unsigned long& i - ) const - /*! - requires - - All the index values in splits are less than feature_pixel_values.size() - - leaf_values.size() is a power of 2. - (i.e. we require a tree with all the levels fully filled out. - - leaf_values.size() == splits.size()+1 - (i.e. there needs to be the right number of leaves given the number of splits in the tree) - ensures - - runs through the tree and returns the vector at the leaf we end up in. - - #i == the selected leaf node index. - !*/ - { - i = 0; - while (i < splits.size()) - { - if ((float)feature_pixel_values[splits[i].idx1] - (float)feature_pixel_values[splits[i].idx2] > splits[i].thresh) - i = left_child(i); - else - i = right_child(i); - } - i = i - splits.size(); - return leaf_values[i]; - } - - friend void serialize (const regression_tree& item, std::ostream& out) - { - dlib::serialize(item.splits, out); - dlib::serialize(item.leaf_values, out); - } - friend void deserialize (regression_tree& item, std::istream& in) - { - dlib::deserialize(item.splits, in); - dlib::deserialize(item.leaf_values, in); - } - }; - - // ------------------------------------------------------------------------------------ - - inline vector<float,2> location ( - const matrix<float,0,1>& shape, - unsigned long idx - ) - /*! - requires - - idx < shape.size()/2 - - shape.size()%2 == 0 - ensures - - returns the idx-th point from the shape vector. - !*/ - { - return vector<float,2>(shape(idx*2), shape(idx*2+1)); - } - - // ------------------------------------------------------------------------------------ - - inline unsigned long nearest_shape_point ( - const matrix<float,0,1>& shape, - const dlib::vector<float,2>& pt - ) - { - // find the nearest part of the shape to this pixel - float best_dist = std::numeric_limits<float>::infinity(); - const unsigned long num_shape_parts = shape.size()/2; - unsigned long best_idx = 0; - for (unsigned long j = 0; j < num_shape_parts; ++j) - { - const float dist = length_squared(location(shape,j)-pt); - if (dist < best_dist) - { - best_dist = dist; - best_idx = j; - } - } - return best_idx; - } - - // ------------------------------------------------------------------------------------ - - inline void create_shape_relative_encoding ( - const matrix<float,0,1>& shape, - const std::vector<dlib::vector<float,2> >& pixel_coordinates, - std::vector<unsigned long>& anchor_idx, - std::vector<dlib::vector<float,2> >& deltas - ) - /*! - requires - - shape.size()%2 == 0 - - shape.size() > 0 - ensures - - #anchor_idx.size() == pixel_coordinates.size() - - #deltas.size() == pixel_coordinates.size() - - for all valid i: - - pixel_coordinates[i] == location(shape,#anchor_idx[i]) + #deltas[i] - !*/ - { - anchor_idx.resize(pixel_coordinates.size()); - deltas.resize(pixel_coordinates.size()); - - - for (unsigned long i = 0; i < pixel_coordinates.size(); ++i) - { - anchor_idx[i] = nearest_shape_point(shape, pixel_coordinates[i]); - deltas[i] = pixel_coordinates[i] - location(shape,anchor_idx[i]); - } - } - - // ------------------------------------------------------------------------------------ - - inline point_transform_affine find_tform_between_shapes ( - const matrix<float,0,1>& from_shape, - const matrix<float,0,1>& to_shape - ) - { - DLIB_ASSERT(from_shape.size() == to_shape.size() && (from_shape.size()%2) == 0 && from_shape.size() > 0,""); - std::vector<vector<float,2> > from_points, to_points; - const unsigned long num = from_shape.size()/2; - from_points.reserve(num); - to_points.reserve(num); - if (num == 1) - { - // Just use an identity transform if there is only one landmark. - return point_transform_affine(); - } - - for (unsigned long i = 0; i < num; ++i) - { - from_points.push_back(location(from_shape,i)); - to_points.push_back(location(to_shape,i)); - } - return find_similarity_transform(from_points, to_points); - } - - // ------------------------------------------------------------------------------------ - - inline point_transform_affine normalizing_tform ( - const rectangle& rect - ) - /*! - ensures - - returns a transform that maps rect.tl_corner() to (0,0) and rect.br_corner() - to (1,1). - !*/ - { - std::vector<vector<float,2> > from_points, to_points; - from_points.push_back(rect.tl_corner()); to_points.push_back(point(0,0)); - from_points.push_back(rect.tr_corner()); to_points.push_back(point(1,0)); - from_points.push_back(rect.br_corner()); to_points.push_back(point(1,1)); - return find_affine_transform(from_points, to_points); - } - - // ------------------------------------------------------------------------------------ - - inline point_transform_affine unnormalizing_tform ( - const rectangle& rect - ) - /*! - ensures - - returns a transform that maps (0,0) to rect.tl_corner() and (1,1) to - rect.br_corner(). - !*/ - { - std::vector<vector<float,2> > from_points, to_points; - to_points.push_back(rect.tl_corner()); from_points.push_back(point(0,0)); - to_points.push_back(rect.tr_corner()); from_points.push_back(point(1,0)); - to_points.push_back(rect.br_corner()); from_points.push_back(point(1,1)); - return find_affine_transform(from_points, to_points); - } - - // ------------------------------------------------------------------------------------ - - template <typename image_type, typename feature_type> - void extract_feature_pixel_values ( - const image_type& img_, - const rectangle& rect, - const matrix<float,0,1>& current_shape, - const matrix<float,0,1>& reference_shape, - const std::vector<unsigned long>& reference_pixel_anchor_idx, - const std::vector<dlib::vector<float,2> >& reference_pixel_deltas, - std::vector<feature_type>& feature_pixel_values - ) - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - reference_pixel_anchor_idx.size() == reference_pixel_deltas.size() - - current_shape.size() == reference_shape.size() - - reference_shape.size()%2 == 0 - - max(mat(reference_pixel_anchor_idx)) < reference_shape.size()/2 - ensures - - #feature_pixel_values.size() == reference_pixel_deltas.size() - - for all valid i: - - #feature_pixel_values[i] == the value of the pixel in img_ that - corresponds to the pixel identified by reference_pixel_anchor_idx[i] - and reference_pixel_deltas[i] when the pixel is located relative to - current_shape rather than reference_shape. - !*/ - { - const matrix<float,2,2> tform = matrix_cast<float>(find_tform_between_shapes(reference_shape, current_shape).get_m()); - const point_transform_affine tform_to_img = unnormalizing_tform(rect); - - const rectangle area = get_rect(img_); - - const_image_view<image_type> img(img_); - feature_pixel_values.resize(reference_pixel_deltas.size()); - for (unsigned long i = 0; i < feature_pixel_values.size(); ++i) - { - // Compute the point in the current shape corresponding to the i-th pixel and - // then map it from the normalized shape space into pixel space. - point p = tform_to_img(tform*reference_pixel_deltas[i] + location(current_shape, reference_pixel_anchor_idx[i])); - if (area.contains(p)) - feature_pixel_values[i] = get_pixel_intensity(img[p.y()][p.x()]); - else - feature_pixel_values[i] = 0; - } - } - - } // end namespace impl - -// ---------------------------------------------------------------------------------------- - - class shape_predictor - { - public: - - - shape_predictor ( - ) - {} - - shape_predictor ( - const matrix<float,0,1>& initial_shape_, - const std::vector<std::vector<impl::regression_tree> >& forests_, - const std::vector<std::vector<dlib::vector<float,2> > >& pixel_coordinates - ) : initial_shape(initial_shape_), forests(forests_) - /*! - requires - - initial_shape.size()%2 == 0 - - forests.size() == pixel_coordinates.size() == the number of cascades - - for all valid i: - - all the index values in forests[i] are less than pixel_coordinates[i].size() - - for all valid i and j: - - forests[i][j].leaf_values.size() is a power of 2. - (i.e. we require a tree with all the levels fully filled out. - - forests[i][j].leaf_values.size() == forests[i][j].splits.size()+1 - (i.e. there need to be the right number of leaves given the number of splits in the tree) - !*/ - { - anchor_idx.resize(pixel_coordinates.size()); - deltas.resize(pixel_coordinates.size()); - // Each cascade uses a different set of pixels for its features. We compute - // their representations relative to the initial shape now and save it. - for (unsigned long i = 0; i < pixel_coordinates.size(); ++i) - impl::create_shape_relative_encoding(initial_shape, pixel_coordinates[i], anchor_idx[i], deltas[i]); - } - - unsigned long num_parts ( - ) const - { - return initial_shape.size()/2; - } - - unsigned long num_features ( - ) const - { - unsigned long num = 0; - for (unsigned long iter = 0; iter < forests.size(); ++iter) - for (unsigned long i = 0; i < forests[iter].size(); ++i) - num += forests[iter][i].num_leaves(); - return num; - } - - template <typename image_type> - full_object_detection operator()( - const image_type& img, - const rectangle& rect - ) const - { - using namespace impl; - matrix<float,0,1> current_shape = initial_shape; - std::vector<float> feature_pixel_values; - for (unsigned long iter = 0; iter < forests.size(); ++iter) - { - extract_feature_pixel_values(img, rect, current_shape, initial_shape, - anchor_idx[iter], deltas[iter], feature_pixel_values); - unsigned long leaf_idx; - // evaluate all the trees at this level of the cascade. - for (unsigned long i = 0; i < forests[iter].size(); ++i) - current_shape += forests[iter][i](feature_pixel_values, leaf_idx); - } - - // convert the current_shape into a full_object_detection - const point_transform_affine tform_to_img = unnormalizing_tform(rect); - std::vector<point> parts(current_shape.size()/2); - for (unsigned long i = 0; i < parts.size(); ++i) - parts[i] = tform_to_img(location(current_shape, i)); - return full_object_detection(rect, parts); - } - - template <typename image_type, typename T, typename U> - full_object_detection operator()( - const image_type& img, - const rectangle& rect, - std::vector<std::pair<T,U> >& feats - ) const - { - feats.clear(); - using namespace impl; - matrix<float,0,1> current_shape = initial_shape; - std::vector<float> feature_pixel_values; - unsigned long feat_offset = 0; - for (unsigned long iter = 0; iter < forests.size(); ++iter) - { - extract_feature_pixel_values(img, rect, current_shape, initial_shape, - anchor_idx[iter], deltas[iter], feature_pixel_values); - // evaluate all the trees at this level of the cascade. - for (unsigned long i = 0; i < forests[iter].size(); ++i) - { - unsigned long leaf_idx; - current_shape += forests[iter][i](feature_pixel_values, leaf_idx); - - feats.push_back(std::make_pair(feat_offset+leaf_idx, 1)); - feat_offset += forests[iter][i].num_leaves(); - } - } - - // convert the current_shape into a full_object_detection - const point_transform_affine tform_to_img = unnormalizing_tform(rect); - std::vector<point> parts(current_shape.size()/2); - for (unsigned long i = 0; i < parts.size(); ++i) - parts[i] = tform_to_img(location(current_shape, i)); - return full_object_detection(rect, parts); - } - - friend void serialize (const shape_predictor& item, std::ostream& out); - - friend void deserialize (shape_predictor& item, std::istream& in); - - private: - matrix<float,0,1> initial_shape; - std::vector<std::vector<impl::regression_tree> > forests; - std::vector<std::vector<unsigned long> > anchor_idx; - std::vector<std::vector<dlib::vector<float,2> > > deltas; - }; - - inline void serialize (const shape_predictor& item, std::ostream& out) - { - int version = 1; - dlib::serialize(version, out); - dlib::serialize(item.initial_shape, out); - dlib::serialize(item.forests, out); - dlib::serialize(item.anchor_idx, out); - dlib::serialize(item.deltas, out); - } - - inline void deserialize (shape_predictor& item, std::istream& in) - { - int version = 0; - dlib::deserialize(version, in); - if (version != 1) - throw serialization_error("Unexpected version found while deserializing dlib::shape_predictor."); - dlib::deserialize(item.initial_shape, in); - dlib::deserialize(item.forests, in); - dlib::deserialize(item.anchor_idx, in); - dlib::deserialize(item.deltas, in); - } - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_array - > - double test_shape_predictor ( - const shape_predictor& sp, - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects, - const std::vector<std::vector<double> >& scales - ) - { - // make sure requires clause is not broken -#ifdef ENABLE_ASSERTS - DLIB_CASSERT( images.size() == objects.size() , - "\t double test_shape_predictor()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t images.size(): " << images.size() - << "\n\t objects.size(): " << objects.size() - ); - for (unsigned long i = 0; i < objects.size(); ++i) - { - for (unsigned long j = 0; j < objects[i].size(); ++j) - { - DLIB_CASSERT(objects[i][j].num_parts() == sp.num_parts(), - "\t double test_shape_predictor()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t objects["<<i<<"]["<<j<<"].num_parts(): " << objects[i][j].num_parts() - << "\n\t sp.num_parts(): " << sp.num_parts() - ); - } - if (scales.size() != 0) - { - DLIB_CASSERT(objects[i].size() == scales[i].size(), - "\t double test_shape_predictor()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t objects["<<i<<"].size(): " << objects[i].size() - << "\n\t scales["<<i<<"].size(): " << scales[i].size() - ); - - } - } -#endif - - running_stats<double> rs; - for (unsigned long i = 0; i < objects.size(); ++i) - { - for (unsigned long j = 0; j < objects[i].size(); ++j) - { - // Just use a scale of 1 (i.e. no scale at all) if the caller didn't supply - // any scales. - const double scale = scales.size()==0 ? 1 : scales[i][j]; - - full_object_detection det = sp(images[i], objects[i][j].get_rect()); - - for (unsigned long k = 0; k < det.num_parts(); ++k) - { - if (objects[i][j].part(k) != OBJECT_PART_NOT_PRESENT) - { - double score = length(det.part(k) - objects[i][j].part(k))/scale; - rs.add(score); - } - } - } - } - return rs.mean(); - } - -// ---------------------------------------------------------------------------------------- - - template < - typename image_array - > - double test_shape_predictor ( - const shape_predictor& sp, - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects - ) - { - std::vector<std::vector<double> > no_scales; - return test_shape_predictor(sp, images, objects, no_scales); - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SHAPE_PREDICToR_H_ - diff --git a/ml/dlib/dlib/image_processing/shape_predictor_abstract.h b/ml/dlib/dlib/image_processing/shape_predictor_abstract.h deleted file mode 100644 index 718b4952e..000000000 --- a/ml/dlib/dlib/image_processing/shape_predictor_abstract.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SHAPE_PREDICToR_ABSTRACT_H_ -#ifdef DLIB_SHAPE_PREDICToR_ABSTRACT_H_ - -#include "full_object_detection_abstract.h" -#include "../matrix.h" -#include "../geometry.h" -#include "../pixel.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class shape_predictor - { - /*! - WHAT THIS OBJECT REPRESENTS - This object is a tool that takes in an image region containing some object - and outputs a set of point locations that define the pose of the object. - The classic example of this is human face pose prediction, where you take - an image of a human face as input and are expected to identify the - locations of important facial landmarks such as the corners of the mouth - and eyes, tip of the nose, and so forth. - - To create useful instantiations of this object you need to use the - shape_predictor_trainer object defined in the - shape_predictor_trainer_abstract.h file to train a shape_predictor using a - set of training images, each annotated with shapes you want to predict. - - THREAD SAFETY - No synchronization is required when using this object. In particular, a - single instance of this object can be used from multiple threads at the - same time. - !*/ - - public: - - shape_predictor ( - ); - /*! - ensures - - #num_parts() == 0 - - #num_features() == 0 - !*/ - - unsigned long num_parts ( - ) const; - /*! - ensures - - returns the number of parts in the shapes predicted by this object. - !*/ - - unsigned long num_features ( - ) const; - /*! - ensures - - Returns the dimensionality of the feature vector output by operator(). - This number is the total number of trees in this object times the number - of leaves on each tree. - !*/ - - template <typename image_type, typename T, typename U> - full_object_detection operator()( - const image_type& img, - const rectangle& rect, - std::vector<std::pair<T,U> >& feats - ) const; - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - - T is some unsigned integral type (e.g. unsigned int). - - U is any scalar type capable of storing the value 1 (e.g. float). - ensures - - Runs the shape prediction algorithm on the part of the image contained in - the given bounding rectangle. So it will try and fit the shape model to - the contents of the given rectangle in the image. For example, if there - is a human face inside the rectangle and you use a face landmarking shape - model then this function will return the locations of the face landmarks - as the parts. So the return value is a full_object_detection DET such - that: - - DET.get_rect() == rect - - DET.num_parts() == num_parts() - - for all valid i: - - DET.part(i) == the location in img for the i-th part of the shape - predicted by this object. - - #feats == a sparse vector that records which leaf each tree used to make - the shape prediction. Moreover, it is an indicator vector, Therefore, - for all valid i: - - #feats[i].second == 1 - Further, #feats is a vector from the space of num_features() dimensional - vectors. The output shape positions can be represented as the dot - product between #feats and a weight vector. Therefore, #feats encodes - all the information from img that was used to predict the returned shape - object. - !*/ - - template <typename image_type> - full_object_detection operator()( - const image_type& img, - const rectangle& rect - ) const; - /*! - requires - - image_type == an image object that implements the interface defined in - dlib/image_processing/generic_image.h - ensures - - Calling this function is equivalent to calling (*this)(img, rect, ignored) - where the 3d argument is discarded. - !*/ - - }; - - void serialize (const shape_predictor& item, std::ostream& out); - void deserialize (shape_predictor& item, std::istream& in); - /*! - provides serialization support - !*/ - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - - template < - typename image_array - > - double test_shape_predictor ( - const shape_predictor& sp, - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects, - const std::vector<std::vector<double> >& scales - ); - /*! - requires - - image_array is a dlib::array of image objects where each image object - implements the interface defined in dlib/image_processing/generic_image.h - - images.size() == objects.size() - - for all valid i and j: - - objects[i][j].num_parts() == sp.num_parts() - - if (scales.size() != 0) then - - There must be a scale value for each full_object_detection in objects. - That is, it must be the case that: - - scales.size() == objects.size() - - for all valid i: - - scales[i].size() == objects[i].size() - ensures - - Tests the given shape_predictor by running it on each of the given objects and - checking how well it recovers the part positions. In particular, for all - valid i and j we perform: - sp(images[i], objects[i][j].get_rect()) - and compare the result with the truth part positions in objects[i][j]. We - then return the average distance (measured in pixels) between a predicted - part location and its true position. - - Note that any parts in objects that are set to OBJECT_PART_NOT_PRESENT are - simply ignored. - - if (scales.size() != 0) then - - Each time we compute the distance between a predicted part location and - its true location in objects[i][j] we divide the distance by - scales[i][j]. Therefore, if you want the reported error to be the - average pixel distance then give an empty scales vector, but if you want - the returned value to be something else like the average distance - normalized by some feature of each object (e.g. the interocular distance) - then you can supply those normalizing values via scales. - !*/ - - template < - typename image_array - > - double test_shape_predictor ( - const shape_predictor& sp, - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects - ); - /*! - requires - - image_array is a dlib::array of image objects where each image object - implements the interface defined in dlib/image_processing/generic_image.h - - images.size() == objects.size() - - for all valid i and j: - - objects[i][j].num_parts() == sp.num_parts() - ensures - - returns test_shape_predictor(sp, images, objects, no_scales) where no_scales - is an empty vector. So this is just a convenience function for calling the - above test_shape_predictor() routine without a scales argument. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SHAPE_PREDICToR_ABSTRACT_H_ - diff --git a/ml/dlib/dlib/image_processing/shape_predictor_trainer.h b/ml/dlib/dlib/image_processing/shape_predictor_trainer.h deleted file mode 100644 index 3090998f9..000000000 --- a/ml/dlib/dlib/image_processing/shape_predictor_trainer.h +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SHAPE_PREDICToR_TRAINER_H_ -#define DLIB_SHAPE_PREDICToR_TRAINER_H_ - -#include "shape_predictor_trainer_abstract.h" -#include "shape_predictor.h" -#include "../console_progress_indicator.h" -#include "../threads.h" -#include "../data_io/image_dataset_metadata.h" -#include "box_overlap_testing.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class shape_predictor_trainer - { - /*! - This thing really only works with unsigned char or rgb_pixel images (since we assume the threshold - should be in the range [-128,128]). - !*/ - public: - - enum padding_mode_t - { - bounding_box_relative, - landmark_relative - }; - - shape_predictor_trainer ( - ) - { - _cascade_depth = 10; - _tree_depth = 4; - _num_trees_per_cascade_level = 500; - _nu = 0.1; - _oversampling_amount = 20; - _feature_pool_size = 400; - _lambda = 0.1; - _num_test_splits = 20; - _feature_pool_region_padding = 0; - _verbose = false; - _num_threads = 0; - _padding_mode = landmark_relative; - } - - unsigned long get_cascade_depth ( - ) const { return _cascade_depth; } - - void set_cascade_depth ( - unsigned long depth - ) - { - DLIB_CASSERT(depth > 0, - "\t void shape_predictor_trainer::set_cascade_depth()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t depth: " << depth - ); - - _cascade_depth = depth; - } - - unsigned long get_tree_depth ( - ) const { return _tree_depth; } - - void set_tree_depth ( - unsigned long depth - ) - { - DLIB_CASSERT(depth > 0, - "\t void shape_predictor_trainer::set_tree_depth()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t depth: " << depth - ); - - _tree_depth = depth; - } - - unsigned long get_num_trees_per_cascade_level ( - ) const { return _num_trees_per_cascade_level; } - - void set_num_trees_per_cascade_level ( - unsigned long num - ) - { - DLIB_CASSERT( num > 0, - "\t void shape_predictor_trainer::set_num_trees_per_cascade_level()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t num: " << num - ); - _num_trees_per_cascade_level = num; - } - - double get_nu ( - ) const { return _nu; } - void set_nu ( - double nu - ) - { - DLIB_CASSERT(0 < nu && nu <= 1, - "\t void shape_predictor_trainer::set_nu()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t nu: " << nu - ); - - _nu = nu; - } - - std::string get_random_seed ( - ) const { return rnd.get_seed(); } - void set_random_seed ( - const std::string& seed - ) { rnd.set_seed(seed); } - - unsigned long get_oversampling_amount ( - ) const { return _oversampling_amount; } - void set_oversampling_amount ( - unsigned long amount - ) - { - DLIB_CASSERT(amount > 0, - "\t void shape_predictor_trainer::set_oversampling_amount()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t amount: " << amount - ); - - _oversampling_amount = amount; - } - - unsigned long get_feature_pool_size ( - ) const { return _feature_pool_size; } - void set_feature_pool_size ( - unsigned long size - ) - { - DLIB_CASSERT(size > 1, - "\t void shape_predictor_trainer::set_feature_pool_size()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t size: " << size - ); - - _feature_pool_size = size; - } - - double get_lambda ( - ) const { return _lambda; } - void set_lambda ( - double lambda - ) - { - DLIB_CASSERT(lambda > 0, - "\t void shape_predictor_trainer::set_lambda()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t lambda: " << lambda - ); - - _lambda = lambda; - } - - unsigned long get_num_test_splits ( - ) const { return _num_test_splits; } - void set_num_test_splits ( - unsigned long num - ) - { - DLIB_CASSERT(num > 0, - "\t void shape_predictor_trainer::set_num_test_splits()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t num: " << num - ); - - _num_test_splits = num; - } - - void set_padding_mode ( - padding_mode_t mode - ) - { - _padding_mode = mode; - } - - padding_mode_t get_padding_mode ( - ) const { return _padding_mode; } - - double get_feature_pool_region_padding ( - ) const { return _feature_pool_region_padding; } - void set_feature_pool_region_padding ( - double padding - ) - { - DLIB_CASSERT(padding > -0.5, - "\t void shape_predictor_trainer::set_feature_pool_region_padding()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t padding: " << padding - ); - - _feature_pool_region_padding = padding; - } - - void be_verbose ( - ) - { - _verbose = true; - } - - void be_quiet ( - ) - { - _verbose = false; - } - - unsigned long get_num_threads ( - ) const { return _num_threads; } - void set_num_threads ( - unsigned long num - ) - { - _num_threads = num; - } - - template <typename image_array> - shape_predictor train ( - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects - ) const - { - using namespace impl; - DLIB_CASSERT(images.size() == objects.size() && images.size() > 0, - "\t shape_predictor shape_predictor_trainer::train()" - << "\n\t Invalid inputs were given to this function. " - << "\n\t images.size(): " << images.size() - << "\n\t objects.size(): " << objects.size() - ); - // make sure the objects agree on the number of parts and that there is at - // least one full_object_detection. - unsigned long num_parts = 0; - std::vector<int> part_present; - for (unsigned long i = 0; i < objects.size(); ++i) - { - for (unsigned long j = 0; j < objects[i].size(); ++j) - { - if (num_parts == 0) - { - num_parts = objects[i][j].num_parts(); - DLIB_CASSERT(objects[i][j].num_parts() != 0, - "\t shape_predictor shape_predictor_trainer::train()" - << "\n\t You can't give objects that don't have any parts to the trainer." - ); - part_present.resize(num_parts); - } - else - { - DLIB_CASSERT(objects[i][j].num_parts() == num_parts, - "\t shape_predictor shape_predictor_trainer::train()" - << "\n\t All the objects must agree on the number of parts. " - << "\n\t objects["<<i<<"]["<<j<<"].num_parts(): " << objects[i][j].num_parts() - << "\n\t num_parts: " << num_parts - ); - } - for (unsigned long p = 0; p < objects[i][j].num_parts(); ++p) - { - if (objects[i][j].part(p) != OBJECT_PART_NOT_PRESENT) - part_present[p] = 1; - } - } - } - DLIB_CASSERT(num_parts != 0, - "\t shape_predictor shape_predictor_trainer::train()" - << "\n\t You must give at least one full_object_detection if you want to train a shape model and it must have parts." - ); - DLIB_CASSERT(sum(mat(part_present)) == (long)num_parts, - "\t shape_predictor shape_predictor_trainer::train()" - << "\n\t Each part must appear at least once in this training data. That is, " - << "\n\t you can't have a part that is always set to OBJECT_PART_NOT_PRESENT." - ); - - // creating thread pool. if num_threads <= 1, trainer should work in caller thread - thread_pool tp(_num_threads > 1 ? _num_threads : 0); - - // determining the type of features used for this type of images - typedef typename std::remove_const<typename std::remove_reference<decltype(images[0])>::type>::type image_type; - typedef typename image_traits<image_type>::pixel_type pixel_type; - typedef typename pixel_traits<pixel_type>::basic_pixel_type feature_type; - - rnd.set_seed(get_random_seed()); - - std::vector<training_sample<feature_type>> samples; - const matrix<float,0,1> initial_shape = populate_training_sample_shapes(objects, samples); - const std::vector<std::vector<dlib::vector<float,2> > > pixel_coordinates = randomly_sample_pixel_coordinates(initial_shape); - - unsigned long trees_fit_so_far = 0; - console_progress_indicator pbar(get_cascade_depth()*get_num_trees_per_cascade_level()); - if (_verbose) - std::cout << "Fitting trees..." << std::endl; - - std::vector<std::vector<impl::regression_tree> > forests(get_cascade_depth()); - // Now start doing the actual training by filling in the forests - for (unsigned long cascade = 0; cascade < get_cascade_depth(); ++cascade) - { - // Each cascade uses a different set of pixels for its features. We compute - // their representations relative to the initial shape first. - std::vector<unsigned long> anchor_idx; - std::vector<dlib::vector<float,2> > deltas; - create_shape_relative_encoding(initial_shape, pixel_coordinates[cascade], anchor_idx, deltas); - - // First compute the feature_pixel_values for each training sample at this - // level of the cascade. - parallel_for(tp, 0, samples.size(), [&](unsigned long i) - { - impl::extract_feature_pixel_values(images[samples[i].image_idx], samples[i].rect, - samples[i].current_shape, initial_shape, anchor_idx, - deltas, samples[i].feature_pixel_values); - }, 1); - - // Now start building the trees at this cascade level. - for (unsigned long i = 0; i < get_num_trees_per_cascade_level(); ++i) - { - forests[cascade].push_back(make_regression_tree(tp, samples, pixel_coordinates[cascade])); - - if (_verbose) - { - ++trees_fit_so_far; - pbar.print_status(trees_fit_so_far); - } - } - } - - if (_verbose) - std::cout << "Training complete " << std::endl; - - return shape_predictor(initial_shape, forests, pixel_coordinates); - } - - private: - - static void object_to_shape ( - const full_object_detection& obj, - matrix<float,0,1>& shape, - matrix<float,0,1>& present // a mask telling which elements of #shape are present. - ) - { - shape.set_size(obj.num_parts()*2); - present.set_size(obj.num_parts()*2); - const point_transform_affine tform_from_img = impl::normalizing_tform(obj.get_rect()); - for (unsigned long i = 0; i < obj.num_parts(); ++i) - { - if (obj.part(i) != OBJECT_PART_NOT_PRESENT) - { - vector<float,2> p = tform_from_img(obj.part(i)); - shape(2*i) = p.x(); - shape(2*i+1) = p.y(); - present(2*i) = 1; - present(2*i+1) = 1; - - if (length(p) > 100) - { - std::cout << "Warning, one of your objects has parts that are way outside its bounding box! This is probably an error in your annotation." << std::endl; - } - } - else - { - shape(2*i) = 0; - shape(2*i+1) = 0; - present(2*i) = 0; - present(2*i+1) = 0; - } - } - } - - template<typename feature_type> - struct training_sample - { - /*! - - CONVENTION - - feature_pixel_values.size() == get_feature_pool_size() - - feature_pixel_values[j] == the value of the j-th feature pool - pixel when you look it up relative to the shape in current_shape. - - - target_shape == The truth shape. Stays constant during the whole - training process (except for the parts that are not present, those are - always equal to the current_shape values). - - present == 0/1 mask saying which parts of target_shape are present. - - rect == the position of the object in the image_idx-th image. All shape - coordinates are coded relative to this rectangle. - - diff_shape == temporary value for holding difference between current - shape and target shape - !*/ - - unsigned long image_idx; - rectangle rect; - matrix<float,0,1> target_shape; - matrix<float,0,1> present; - - matrix<float,0,1> current_shape; - matrix<float,0,1> diff_shape; - std::vector<feature_type> feature_pixel_values; - - void swap(training_sample& item) - { - std::swap(image_idx, item.image_idx); - std::swap(rect, item.rect); - target_shape.swap(item.target_shape); - present.swap(item.present); - current_shape.swap(item.current_shape); - diff_shape.swap(item.diff_shape); - feature_pixel_values.swap(item.feature_pixel_values); - } - }; - - template<typename feature_type> - impl::regression_tree make_regression_tree ( - thread_pool& tp, - std::vector<training_sample<feature_type>>& samples, - const std::vector<dlib::vector<float,2> >& pixel_coordinates - ) const - { - using namespace impl; - std::deque<std::pair<unsigned long, unsigned long> > parts; - parts.push_back(std::make_pair(0, (unsigned long)samples.size())); - - impl::regression_tree tree; - - // walk the tree in breadth first order - const unsigned long num_split_nodes = static_cast<unsigned long>(std::pow(2.0, (double)get_tree_depth())-1); - std::vector<matrix<float,0,1> > sums(num_split_nodes*2+1); - if (tp.num_threads_in_pool() > 1) - { - // Here we need to calculate shape differences and store sum of differences into sums[0] - // to make it. I am splitting samples into blocks, each block will be processed by - // separate thread, and the sum of differences of each block is stored into separate - // place in block_sums - - const unsigned long num_workers = std::max(1UL, tp.num_threads_in_pool()); - const unsigned long num = samples.size(); - const unsigned long block_size = std::max(1UL, (num + num_workers - 1) / num_workers); - std::vector<matrix<float,0,1> > block_sums(num_workers); - - parallel_for(tp, 0, num_workers, [&](unsigned long block) - { - const unsigned long block_begin = block * block_size; - const unsigned long block_end = std::min(num, block_begin + block_size); - for (unsigned long i = block_begin; i < block_end; ++i) - { - samples[i].diff_shape = samples[i].target_shape - samples[i].current_shape; - block_sums[block] += samples[i].diff_shape; - } - }, 1); - - // now calculate the total result from separate blocks - for (unsigned long i = 0; i < block_sums.size(); ++i) - sums[0] += block_sums[i]; - } - else - { - // synchronous implementation - for (unsigned long i = 0; i < samples.size(); ++i) - { - samples[i].diff_shape = samples[i].target_shape - samples[i].current_shape; - sums[0] += samples[i].diff_shape; - } - } - - for (unsigned long i = 0; i < num_split_nodes; ++i) - { - std::pair<unsigned long,unsigned long> range = parts.front(); - parts.pop_front(); - - const impl::split_feature split = generate_split(tp, samples, range.first, - range.second, pixel_coordinates, sums[i], sums[left_child(i)], - sums[right_child(i)]); - tree.splits.push_back(split); - const unsigned long mid = partition_samples(split, samples, range.first, range.second); - - parts.push_back(std::make_pair(range.first, mid)); - parts.push_back(std::make_pair(mid, range.second)); - } - - // Now all the parts contain the ranges for the leaves so we can use them to - // compute the average leaf values. - matrix<float,0,1> present_counts(samples[0].target_shape.size()); - tree.leaf_values.resize(parts.size()); - for (unsigned long i = 0; i < parts.size(); ++i) - { - // Get the present counts for each dimension so we can divide each - // dimension by the number of observations we have on it to find the mean - // displacement in each leaf. - present_counts = 0; - for (unsigned long j = parts[i].first; j < parts[i].second; ++j) - present_counts += samples[j].present; - present_counts = dlib::reciprocal(present_counts); - - if (parts[i].second != parts[i].first) - tree.leaf_values[i] = pointwise_multiply(present_counts,sums[num_split_nodes+i]*get_nu()); - else - tree.leaf_values[i] = zeros_matrix(samples[0].target_shape); - - // now adjust the current shape based on these predictions - parallel_for(tp, parts[i].first, parts[i].second, [&](unsigned long j) - { - samples[j].current_shape += tree.leaf_values[i]; - // For parts that aren't present in the training data, we just make - // sure that the target shape always matches and therefore gives zero - // error. So this makes the algorithm simply ignore non-present - // landmarks. - for (long k = 0; k < samples[j].present.size(); ++k) - { - // if this part is not present - if (samples[j].present(k) == 0) - samples[j].target_shape(k) = samples[j].current_shape(k); - } - }, 1); - } - - return tree; - } - - impl::split_feature randomly_generate_split_feature ( - const std::vector<dlib::vector<float,2> >& pixel_coordinates - ) const - { - const double lambda = get_lambda(); - impl::split_feature feat; - const size_t max_iters = get_feature_pool_size()*get_feature_pool_size(); - for (size_t i = 0; i < max_iters; ++i) - { - feat.idx1 = rnd.get_integer(get_feature_pool_size()); - feat.idx2 = rnd.get_integer(get_feature_pool_size()); - while (feat.idx1 == feat.idx2) - feat.idx2 = rnd.get_integer(get_feature_pool_size()); - const double dist = length(pixel_coordinates[feat.idx1]-pixel_coordinates[feat.idx2]); - const double accept_prob = std::exp(-dist/lambda); - if (accept_prob > rnd.get_random_double()) - break; - } - - feat.thresh = (rnd.get_random_double()*256 - 128)/2.0; - - return feat; - } - - template<typename feature_type> - impl::split_feature generate_split ( - thread_pool& tp, - const std::vector<training_sample<feature_type>>& samples, - unsigned long begin, - unsigned long end, - const std::vector<dlib::vector<float,2> >& pixel_coordinates, - const matrix<float,0,1>& sum, - matrix<float,0,1>& left_sum, - matrix<float,0,1>& right_sum - ) const - { - // generate a bunch of random splits and test them and return the best one. - - const unsigned long num_test_splits = get_num_test_splits(); - - // sample the random features we test in this function - std::vector<impl::split_feature> feats; - feats.reserve(num_test_splits); - for (unsigned long i = 0; i < num_test_splits; ++i) - feats.push_back(randomly_generate_split_feature(pixel_coordinates)); - - std::vector<matrix<float,0,1> > left_sums(num_test_splits); - std::vector<unsigned long> left_cnt(num_test_splits); - - const unsigned long num_workers = std::max(1UL, tp.num_threads_in_pool()); - const unsigned long block_size = std::max(1UL, (num_test_splits + num_workers - 1) / num_workers); - - // now compute the sums of vectors that go left for each feature - parallel_for(tp, 0, num_workers, [&](unsigned long block) - { - const unsigned long block_begin = block * block_size; - const unsigned long block_end = std::min(block_begin + block_size, num_test_splits); - - for (unsigned long j = begin; j < end; ++j) - { - for (unsigned long i = block_begin; i < block_end; ++i) - { - if ((float)samples[j].feature_pixel_values[feats[i].idx1] - (float)samples[j].feature_pixel_values[feats[i].idx2] > feats[i].thresh) - { - left_sums[i] += samples[j].diff_shape; - ++left_cnt[i]; - } - } - } - - }, 1); - - // now figure out which feature is the best - double best_score = -1; - unsigned long best_feat = 0; - matrix<float,0,1> temp; - for (unsigned long i = 0; i < num_test_splits; ++i) - { - // check how well the feature splits the space. - double score = 0; - unsigned long right_cnt = end-begin-left_cnt[i]; - if (left_cnt[i] != 0 && right_cnt != 0) - { - temp = sum - left_sums[i]; - score = dot(left_sums[i],left_sums[i])/left_cnt[i] + dot(temp,temp)/right_cnt; - if (score > best_score) - { - best_score = score; - best_feat = i; - } - } - } - - left_sums[best_feat].swap(left_sum); - if (left_sum.size() != 0) - { - right_sum = sum - left_sum; - } - else - { - right_sum = sum; - left_sum = zeros_matrix(sum); - } - return feats[best_feat]; - } - - template<typename feature_type> - unsigned long partition_samples ( - const impl::split_feature& split, - std::vector<training_sample<feature_type>>& samples, - unsigned long begin, - unsigned long end - ) const - { - // splits samples based on split (sorta like in quick sort) and returns the mid - // point. make sure you return the mid in a way compatible with how we walk - // through the tree. - - unsigned long i = begin; - for (unsigned long j = begin; j < end; ++j) - { - if ((float)samples[j].feature_pixel_values[split.idx1] - (float)samples[j].feature_pixel_values[split.idx2] > split.thresh) - { - samples[i].swap(samples[j]); - ++i; - } - } - return i; - } - - - - template<typename feature_type> - matrix<float,0,1> populate_training_sample_shapes( - const std::vector<std::vector<full_object_detection> >& objects, - std::vector<training_sample<feature_type>>& samples - ) const - { - samples.clear(); - matrix<float,0,1> mean_shape; - matrix<float,0,1> count; - // first fill out the target shapes - for (unsigned long i = 0; i < objects.size(); ++i) - { - for (unsigned long j = 0; j < objects[i].size(); ++j) - { - training_sample<feature_type> sample; - sample.image_idx = i; - sample.rect = objects[i][j].get_rect(); - object_to_shape(objects[i][j], sample.target_shape, sample.present); - for (unsigned long itr = 0; itr < get_oversampling_amount(); ++itr) - samples.push_back(sample); - mean_shape += sample.target_shape; - count += sample.present; - } - } - - mean_shape = pointwise_multiply(mean_shape,reciprocal(count)); - - // now go pick random initial shapes - for (unsigned long i = 0; i < samples.size(); ++i) - { - if ((i%get_oversampling_amount()) == 0) - { - // The mean shape is what we really use as an initial shape so always - // include it in the training set as an example starting shape. - samples[i].current_shape = mean_shape; - } - else - { - samples[i].current_shape.set_size(0); - - matrix<float,0,1> hits(mean_shape.size()); - hits = 0; - - int iter = 0; - // Pick a few samples at random and randomly average them together to - // make the initial shape. Note that we make sure we get at least one - // observation (i.e. non-OBJECT_PART_NOT_PRESENT) on each part - // location. - while(min(hits) == 0 || iter < 2) - { - ++iter; - const unsigned long rand_idx = rnd.get_random_32bit_number()%samples.size(); - const double alpha = rnd.get_random_double()+0.1; - samples[i].current_shape += alpha*samples[rand_idx].target_shape; - hits += alpha*samples[rand_idx].present; - } - samples[i].current_shape = pointwise_multiply(samples[i].current_shape, reciprocal(hits)); - } - - } - for (unsigned long i = 0; i < samples.size(); ++i) - { - for (long k = 0; k < samples[i].present.size(); ++k) - { - // if this part is not present - if (samples[i].present(k) == 0) - samples[i].target_shape(k) = samples[i].current_shape(k); - } - } - - - return mean_shape; - } - - - void randomly_sample_pixel_coordinates ( - std::vector<dlib::vector<float,2> >& pixel_coordinates, - const double min_x, - const double min_y, - const double max_x, - const double max_y - ) const - /*! - ensures - - #pixel_coordinates.size() == get_feature_pool_size() - - for all valid i: - - pixel_coordinates[i] == a point in the box defined by the min/max x/y arguments. - !*/ - { - pixel_coordinates.resize(get_feature_pool_size()); - for (unsigned long i = 0; i < get_feature_pool_size(); ++i) - { - pixel_coordinates[i].x() = rnd.get_random_double()*(max_x-min_x) + min_x; - pixel_coordinates[i].y() = rnd.get_random_double()*(max_y-min_y) + min_y; - } - } - - std::vector<std::vector<dlib::vector<float,2> > > randomly_sample_pixel_coordinates ( - const matrix<float,0,1>& initial_shape - ) const - { - const double padding = get_feature_pool_region_padding(); - // Figure out the bounds on the object shapes. We will sample uniformly - // from this box. - matrix<float> temp = reshape(initial_shape, initial_shape.size()/2, 2); - double min_x = min(colm(temp,0)); - double min_y = min(colm(temp,1)); - double max_x = max(colm(temp,0)); - double max_y = max(colm(temp,1)); - - if (get_padding_mode() == bounding_box_relative) - { - min_x = std::min(0.0, min_x); - min_y = std::min(0.0, min_y); - max_x = std::max(1.0, max_x); - max_y = std::max(1.0, max_y); - } - - min_x -= padding; - min_y -= padding; - max_x += padding; - max_y += padding; - - std::vector<std::vector<dlib::vector<float,2> > > pixel_coordinates; - pixel_coordinates.resize(get_cascade_depth()); - for (unsigned long i = 0; i < get_cascade_depth(); ++i) - randomly_sample_pixel_coordinates(pixel_coordinates[i], min_x, min_y, max_x, max_y); - return pixel_coordinates; - } - - - - mutable dlib::rand rnd; - - unsigned long _cascade_depth; - unsigned long _tree_depth; - unsigned long _num_trees_per_cascade_level; - double _nu; - unsigned long _oversampling_amount; - unsigned long _feature_pool_size; - double _lambda; - unsigned long _num_test_splits; - double _feature_pool_region_padding; - bool _verbose; - unsigned long _num_threads; - padding_mode_t _padding_mode; - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename some_type_of_rectangle - > - image_dataset_metadata::dataset make_bounding_box_regression_training_data ( - const image_dataset_metadata::dataset& truth, - const std::vector<std::vector<some_type_of_rectangle>>& detections - ) - { - DLIB_CASSERT(truth.images.size() == detections.size(), - "truth.images.size(): "<< truth.images.size() << - "\tdetections.size(): "<< detections.size() - ); - image_dataset_metadata::dataset result = truth; - - for (size_t i = 0; i < truth.images.size(); ++i) - { - result.images[i].boxes.clear(); - for (auto truth_box : truth.images[i].boxes) - { - if (truth_box.ignore) - continue; - - // Find the detection that best matches the current truth_box. - auto det = max_scoring_element(detections[i], [&truth_box](const rectangle& r) { return box_intersection_over_union(r, truth_box.rect); }); - if (det.second > 0.5) - { - // Remove any existing parts and replace them with the truth_box corners. - truth_box.parts.clear(); - auto b = truth_box.rect; - truth_box.parts["left"] = (b.tl_corner()+b.bl_corner())/2; - truth_box.parts["right"] = (b.tr_corner()+b.br_corner())/2; - truth_box.parts["top"] = (b.tl_corner()+b.tr_corner())/2; - truth_box.parts["bottom"] = (b.bl_corner()+b.br_corner())/2; - truth_box.parts["middle"] = center(b); - - // Now replace the bounding truth_box with the detector's bounding truth_box. - truth_box.rect = det.first; - - result.images[i].boxes.push_back(truth_box); - } - } - } - return result; - } - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SHAPE_PREDICToR_TRAINER_H_ - diff --git a/ml/dlib/dlib/image_processing/shape_predictor_trainer_abstract.h b/ml/dlib/dlib/image_processing/shape_predictor_trainer_abstract.h deleted file mode 100644 index 278b97842..000000000 --- a/ml/dlib/dlib/image_processing/shape_predictor_trainer_abstract.h +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (C) 2014 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_SHAPE_PREDICToR_TRAINER_ABSTRACT_H_ -#ifdef DLIB_SHAPE_PREDICToR_TRAINER_ABSTRACT_H_ - -#include "shape_predictor_abstract.h" -#include "../data_io/image_dataset_metadata.h" - -namespace dlib -{ - -// ---------------------------------------------------------------------------------------- - - class shape_predictor_trainer - { - /*! - WHAT THIS OBJECT REPRESENTS - This object is a tool for training shape_predictors based on annotated training - images. Its implementation uses the algorithm described in: - One Millisecond Face Alignment with an Ensemble of Regression Trees - by Vahid Kazemi and Josephine Sullivan, CVPR 2014 - - !*/ - - public: - - shape_predictor_trainer ( - ); - /*! - ensures - - #get_cascade_depth() == 10 - - #get_tree_depth() == 4 - - #get_num_trees_per_cascade_level() == 500 - - #get_nu() == 0.1 - - #get_oversampling_amount() == 20 - - #get_feature_pool_size() == 400 - - #get_lambda() == 0.1 - - #get_num_test_splits() == 20 - - #get_feature_pool_region_padding() == 0 - - #get_random_seed() == "" - - #get_num_threads() == 0 - - #get_padding_mode() == landmark_relative - - This object will not be verbose - !*/ - - unsigned long get_cascade_depth ( - ) const; - /*! - ensures - - returns the number of cascades created when you train a model. This - means that the total number of trees in the learned model is equal to - get_cascade_depth()*get_num_trees_per_cascade_level(). - !*/ - - void set_cascade_depth ( - unsigned long depth - ); - /*! - requires - - depth > 0 - ensures - - #get_cascade_depth() == depth - !*/ - - unsigned long get_tree_depth ( - ) const; - /*! - ensures - - returns the depth of the trees used in the cascade. In particular, there - are pow(2,get_tree_depth()) leaves in each tree. - !*/ - - void set_tree_depth ( - unsigned long depth - ); - /*! - requires - - depth > 0 - ensures - - #get_tree_depth() == depth - !*/ - - unsigned long get_num_trees_per_cascade_level ( - ) const; - /*! - ensures - - returns the number of trees created for each cascade. This means that - the total number of trees in the learned model is equal to - get_cascade_depth()*get_num_trees_per_cascade_level(). - !*/ - - void set_num_trees_per_cascade_level ( - unsigned long num - ); - /*! - requires - - num > 0 - ensures - - #get_num_trees_per_cascade_level() == num - !*/ - - double get_nu ( - ) const; - /*! - ensures - - returns the regularization parameter. Larger values of this parameter - will cause the algorithm to fit the training data better but may also - cause overfitting. - !*/ - - void set_nu ( - double nu - ); - /*! - requires - - 0 < nu <= 1 - ensures - - #get_nu() == nu - !*/ - - std::string get_random_seed ( - ) const; - /*! - ensures - - returns the random seed used by the internal random number generator. - Since this algorithm is a random forest style algorithm it relies on a - random number generator for generating the trees. So each setting of the - random seed will produce slightly different outputs. - !*/ - - void set_random_seed ( - const std::string& seed - ); - /*! - ensures - - #get_random_seed() == seed - !*/ - - unsigned long get_oversampling_amount ( - ) const; - /*! - ensures - - You give annotated images to this object as training examples. You - can effectively increase the amount of training data by adding in each - training example multiple times but with a randomly selected deformation - applied to it. That is what this parameter controls. That is, if you - supply N training samples to train() then the algorithm runs internally - with N*get_oversampling_amount() training samples. So the bigger this - parameter the better (excepting that larger values make training take - longer). In terms of the Kazemi paper, this parameter is the number of - randomly selected initial starting points sampled for each training - example. - !*/ - - void set_oversampling_amount ( - unsigned long amount - ); - /*! - requires - - amount > 0 - ensures - - #get_oversampling_amount() == amount - !*/ - - unsigned long get_feature_pool_size ( - ) const; - /*! - ensures - - At each level of the cascade we randomly sample get_feature_pool_size() - pixels from the image. These pixels are used to generate features for - the random trees. So in general larger settings of this parameter give - better accuracy but make the algorithm run slower. - !*/ - - void set_feature_pool_size ( - unsigned long size - ); - /*! - requires - - size > 1 - ensures - - #get_feature_pool_size() == size - !*/ - - enum padding_mode_t - { - bounding_box_relative, - landmark_relative - }; - - padding_mode_t get_padding_mode ( - ) const; - /*! - ensures - - returns the current padding mode. See get_feature_pool_region_padding() - for a discussion of the modes. - !*/ - - void set_padding_mode ( - padding_mode_t mode - ); - /*! - ensures - - #get_padding_mode() == mode - !*/ - - double get_feature_pool_region_padding ( - ) const; - /*! - ensures - - This algorithm works by comparing the relative intensity of pairs of - pixels in the input image. To decide which pixels to look at, the - training algorithm randomly selects pixels from a box roughly centered - around the object of interest. We call this box the feature pool region - box. - - Each object of interest is defined by a full_object_detection, which - contains a bounding box and a list of landmarks. If - get_padding_mode()==landmark_relative then the feature pool region box is - the tightest box that contains the landmarks inside the - full_object_detection. In this mode the full_object_detection's bounding - box is ignored. Otherwise, if the padding mode is bounding_box_relative - then the feature pool region box is the tightest box that contains BOTH - the landmarks and the full_object_detection's bounding box. - - Additionally, you can adjust the size of the feature pool padding region - by setting get_feature_pool_region_padding() to some value. If - get_feature_pool_region_padding()==0 then the feature pool region box is - unmodified and defined exactly as stated above. However, you can expand - the size of the box by setting the padding > 0 or shrink it by setting it - to something < 0. - - To explain this precisely, for a padding of 0 we say that the pixels are - sampled from a box of size 1x1. The padding value is added to each side - of the box. So a padding of 0.5 would cause the algorithm to sample - pixels from a box that was 2x2, effectively multiplying the area pixels - are sampled from by 4. Similarly, setting the padding to -0.2 would - cause it to sample from a box 0.6x0.6 in size. - !*/ - - void set_feature_pool_region_padding ( - double padding - ); - /*! - requires - - padding > -0.5 - ensures - - #get_feature_pool_region_padding() == padding - !*/ - - double get_lambda ( - ) const; - /*! - ensures - - To decide how to split nodes in the regression trees the algorithm looks - at pairs of pixels in the image. These pixel pairs are sampled randomly - but with a preference for selecting pixels that are near each other. - get_lambda() controls this "nearness" preference. In particular, smaller - values of get_lambda() will make the algorithm prefer to select pixels - close together and larger values of get_lambda() will make it care less - about picking nearby pixel pairs. - - Note that this is the inverse of how it is defined in the Kazemi paper. - For this object, you should think of lambda as "the fraction of the - bounding box will we traverse to find a neighboring pixel". Nominally, - this is normalized between 0 and 1. So reasonable settings of lambda are - values in the range 0 < lambda < 1. - !*/ - - void set_lambda ( - double lambda - ); - /*! - requires - - lambda > 0 - ensures - - #get_lambda() == lambda - !*/ - - unsigned long get_num_test_splits ( - ) const; - /*! - ensures - - When generating the random trees we randomly sample get_num_test_splits() - possible split features at each node and pick the one that gives the best - split. Larger values of this parameter will usually give more accurate - outputs but take longer to train. - !*/ - - void set_num_test_splits ( - unsigned long num - ); - /*! - requires - - num > 0 - ensures - - #get_num_test_splits() == num - !*/ - - unsigned long get_num_threads ( - ) const; - /*! - ensures - - When running training process, it is possible to make some parts of it parallel - using CPU threads with #parallel_for() extension and creating #thread_pool internally - When get_num_threads() == 0, trainer will not create threads and all processing will - be done in the calling thread - !*/ - - void set_num_threads ( - unsigned long num - ); - /*! - requires - - num >= 0 - ensures - - #get_num_threads() == num - !*/ - - void be_verbose ( - ); - /*! - ensures - - This object will print status messages to standard out so that a - user can observe the progress of the algorithm. - !*/ - - void be_quiet ( - ); - /*! - ensures - - This object will not print anything to standard out - !*/ - - template <typename image_array> - shape_predictor train ( - const image_array& images, - const std::vector<std::vector<full_object_detection> >& objects - ) const; - /*! - requires - - image_array is a dlib::array of image objects where each image object - implements the interface defined in dlib/image_processing/generic_image.h - - images.size() == objects.size() - - images.size() > 0 - - for some i: objects[i].size() != 0 - (i.e. there has to be at least one full_object_detection in the training set) - - for all valid p, there must exist i and j such that: - objects[i][j].part(p) != OBJECT_PART_NOT_PRESENT. - (i.e. You can't define a part that is always set to OBJECT_PART_NOT_PRESENT.) - - for all valid i,j,k,l: - - objects[i][j].num_parts() == objects[k][l].num_parts() - (i.e. all objects must agree on the number of parts) - - objects[i][j].num_parts() > 0 - ensures - - This object will try to learn to predict the locations of an object's parts - based on the object bounding box (i.e. full_object_detection::get_rect()) - and the image pixels in that box. That is, we will try to learn a - shape_predictor, SP, such that: - SP(images[i], objects[i][j].get_rect()) == objects[i][j] - This learned SP object is then returned. - - Not all parts are required to be observed for all objects. So if you - have training instances with missing parts then set the part positions - equal to OBJECT_PART_NOT_PRESENT and this algorithm will basically ignore - those missing parts. - !*/ - }; - -// ---------------------------------------------------------------------------------------- - - template < - typename some_type_of_rectangle - > - image_dataset_metadata::dataset make_bounding_box_regression_training_data ( - const image_dataset_metadata::dataset& truth, - const std::vector<std::vector<some_type_of_rectangle>>& detections - ); - /*! - requires - - truth.images.size() == detections.size() - - some_type_of_rectangle == rectangle, drectangle, mmod_rect, or any other type - that is convertible to a rectangle. - ensures - - Suppose you have an object detector that can roughly locate objects in an - image. This means your detector draws boxes around objects, but these are - *rough* boxes in the sense that they aren't positioned super accurately. For - instance, HOG based detectors usually have a stride of 8 pixels. So the - positional accuracy is going to be, at best, +/-8 pixels. - - If you want to get better positional accuracy one easy thing to do is train a - shape_predictor to give you the location of the object's box. The - make_bounding_box_regression_training_data() routine helps you do this by - creating an appropriate training dataset. It does this by taking the dataset - you used to train your detector (given by the truth object), and combining - that with the output of your detector on each image in the training dataset - (given by the detections object). In particular, it will create a new - annotated dataset where each object box is one of the rectangles from - detections and that object has 5 part annotations. These annotations - identify the sides and middle of the truth rectangle corresponding to the - detection rectangle. You can then take the returned dataset and train a - shape_predictor on it. The resulting shape_predictor can then be used to do - bounding box regression. - - As an aside, the reason we create 5 part annotations in this way is because - it gives the best shape_predictor when trained. If instead you used the 4 - corners it wouldn't work as well, due to tedious vagaries of the shape_predictor - training process. - - - We assume that detections[i] contains object detections corresponding to - the image truth.images[i]. - !*/ - -// ---------------------------------------------------------------------------------------- - -} - -#endif // DLIB_SHAPE_PREDICToR_TRAINER_ABSTRACT_H_ - |