From 9e54a2088a48052728f84a90a7645b2b5ba41982 Mon Sep 17 00:00:00 2001 From: Davis King Date: Mon, 4 Sep 2017 21:56:24 -0400 Subject: [PATCH] Refactored create_tiled_pyramid() code to remove unnecessary copying. It's now about 2x faster. The code is cleaner too. --- dlib/image_transforms/image_pyramid.h | 181 +++++++++++++++----------- 1 file changed, 108 insertions(+), 73 deletions(-) diff --git a/dlib/image_transforms/image_pyramid.h b/dlib/image_transforms/image_pyramid.h index 02911a616..3efed30d8 100644 --- a/dlib/image_transforms/image_pyramid.h +++ b/dlib/image_transforms/image_pyramid.h @@ -1021,6 +1021,100 @@ namespace dlib nc = 0; } +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template + void compute_tiled_image_pyramid_details ( + const pyramid_type& pyr, + long nr, + long nc, + const unsigned long padding, + const unsigned long outer_padding, + std::vector& rects, + long& pyramid_image_nr, + long& pyramid_image_nc + ) + { + rects.clear(); + if (nr*nc == 0) + { + pyramid_image_nr = 0; + pyramid_image_nc = 0; + return; + } + + const long min_height = 5; + rects.reserve(100); + rects.push_back(rectangle(nc,nr)); + // build the whole pyramid + while(true) + { + find_pyramid_down_output_image_size(pyr, nr, nc); + if (nr*nc == 0 || nr < min_height) + break; + rects.push_back(rectangle(nc,nr)); + } + + // figure out output image size + long total_height = 0; + for (auto&& i : rects) + total_height += i.height()+padding; + total_height -= padding*2; // don't add unnecessary padding to the very right side. + long height = 0; + long prev_width = 0; + for (auto&& i : rects) + { + // Figure out how far we go on the first column. We go until the next image can + // fit next to the previous one, which means we can double back for the second + // column of images. + if (i.width() <= rects[0].width()-prev_width-(long)padding && + (height-rects[0].height())*2 >= (total_height-rects[0].height())) + { + break; + } + height += i.height() + padding; + prev_width = i.width(); + } + height -= padding; // don't add unnecessary padding to the very right side. + + const long width = rects[0].width(); + pyramid_image_nr = height+outer_padding*2; + pyramid_image_nc = width+outer_padding*2; + + + long y = outer_padding; + size_t i = 0; + while(y < height+(long)outer_padding && i < rects.size()) + { + rects[i] = translate_rect(rects[i],point(outer_padding,y)); + DLIB_ASSERT(rectangle(pyramid_image_nc,pyramid_image_nr).contains(rects[i])); + y += rects[i].height()+padding; + ++i; + } + y -= padding; + while (i < rects.size()) + { + point p1(outer_padding+width-1,y-1); + point p2 = p1 - rects[i].br_corner(); + rectangle rect(p1,p2); + DLIB_ASSERT(rectangle(pyramid_image_nc,pyramid_image_nr).contains(rect)); + // don't keep going on the last row if it would intersect the original image. + if (!rects[0].intersect(rect).is_empty()) + break; + + rects[i] = rect; + y -= rects[i].height()+padding; + ++i; + } + + // Delete any extraneous rectangles if we broke out of the above loop early due to + // intersection with the original image. + rects.resize(i); + } + } + // ---------------------------------------------------------------------------------------- template < @@ -1038,84 +1132,25 @@ namespace dlib { DLIB_ASSERT(!is_same_object(img, out_img)); - rects.clear(); - if (num_rows(img)*num_columns(img) == 0) - { - set_image_size(out_img,0,0); - return; - } - - const long min_height = 5; + long out_nr, out_nc; pyramid_type pyr; - std::vector> pyramid; - matrix temp; - assign_image(temp, img); - pyramid.push_back(std::move(temp)); - // build the whole pyramid - while(true) - { - matrix temp; - pyr(pyramid.back(), temp); - if (temp.size() == 0 || temp.nr() < min_height) - break; - pyramid.push_back(std::move(temp)); - } + impl::compute_tiled_image_pyramid_details(pyr, img.nr(), img.nc(), padding, outer_padding, rects, out_nr, out_nc); - // figure out output image size - long total_height = 0; - for (auto&& i : pyramid) - total_height += i.nr()+padding; - total_height -= padding*2; // don't add unnecessary padding to the very right side. - long height = 0; - long prev_width = 0; - for (auto&& i : pyramid) - { - // Figure out how far we go on the first column. We go until the next image can - // fit next to the previous one, which means we can double back for the second - // column of images. - if (i.nc() <= img.nc()-prev_width-(long)padding && - (height-img.nr())*2 >= (total_height-img.nr())) - { - break; - } - height += i.nr() + padding; - prev_width = i.nc(); - } - height -= padding; // don't add unnecessary padding to the very right side. - - const long width = img.nc(); - set_image_size(out_img,height+outer_padding*2,width+outer_padding*2); + set_image_size(out_img, out_nr, out_nc); assign_all_pixels(out_img, 0); - long y = outer_padding; - size_t i = 0; - while(y < height+(long)outer_padding) - { - rectangle rect = translate_rect(get_rect(pyramid[i]),point(outer_padding,y)); - DLIB_ASSERT(get_rect(out_img).contains(rect)); - rects.push_back(rect); - auto si = sub_image(out_img, rect); - assign_image(si, pyramid[i]); - y += pyramid[i].nr()+padding; - ++i; - } - y -= padding; - while (i < pyramid.size()) - { - point p1(outer_padding+width-1,y-1); - point p2 = p1 - get_rect(pyramid[i]).br_corner(); - rectangle rect(p1,p2); - DLIB_ASSERT(get_rect(out_img).contains(rect)); - // don't keep going on the last row if it would intersect the original image. - if (!get_rect(img).intersect(rect).is_empty()) - break; - rects.push_back(rect); - auto si = sub_image(out_img, rect); - assign_image(si, pyramid[i]); - y -= pyramid[i].nr()+padding; - ++i; - } + if (rects.size() == 0) + return; + // now build the image pyramid into out_img + auto si = sub_image(out_img, rects[0]); + assign_image(si, img); + for (size_t i = 1; i < rects.size(); ++i) + { + auto s1 = sub_image(out_img, rects[i-1]); + auto s2 = sub_image(out_img, rects[i]); + pyr(s1,s2); + } } // ----------------------------------------------------------------------------------------