diff --git a/include/hdrplus/align.h b/include/hdrplus/align.h index 18a4784..4d7391f 100644 --- a/include/hdrplus/align.h +++ b/include/hdrplus/align.h @@ -26,7 +26,12 @@ class align std::vector>>>& aligements ); private: - const std::vector inv_scale_factors = { 1, 2, 2, 4 }; + // From original image to coarse image + const std::vector inv_scale_factors = { 1, 2, 4, 4 }; + const std::vector distances = { 1, 2, 2, 2 }; // L1 / L2 distance + const std::vector search_radious = { 1, 4, 4, 4 }; + const std::vector tile_sizes = { 16, 16, 16, 8 }; + const int num_levels = 4; }; } // namespace hdrplus diff --git a/include/hdrplus/utility.h b/include/hdrplus/utility.h index d3ee40f..ee87631 100644 --- a/include/hdrplus/utility.h +++ b/include/hdrplus/utility.h @@ -5,6 +5,14 @@ #include // all opencv header // TODO: add openmp support +#if defined(__clang__) + #define LOOP_UNROLL unroll +#elif defined(__GNUC__) || defined(__GNUG__) + #define LOOP_UNROLL GCC unroll +#elif defined(_MSC_VER) + #define LOOP_UNROLL unroll +#endif + namespace hdrplus { @@ -68,10 +76,10 @@ cv::Mat box_filter_kxk( cv::Mat src_image ) { T box_sum = T(0); - #pragma GCC unroll kernel + #pragma LOOP_UNROLL for ( int kernel_row_i = 0; kernel_row_i < kernel; ++kernel_row_i ) { - #pragma GCC unroll kernel + #pragma LOOP_UNROLL for ( int kernel_col_i = 0; kernel_col_i < kernel; ++kernel_col_i ) { box_sum += src_image_ptr[ ( row_i + kernel_row_i ) * src_step + ( col_i + kernel_col_i ) ]; diff --git a/src/align.cpp b/src/align.cpp index d8a9be2..f15ab4a 100644 --- a/src/align.cpp +++ b/src/align.cpp @@ -51,20 +51,174 @@ static void build_image_pyramid( \ } } + +template< int stride > +static void upsample_alignment_stride( \ + std::vector>>& src_alignment, \ + std::vector>>& dst_alignment, + int scale_factor ) +{ + int src_height = src_alignment.size(); + int src_width = src_alignment[ 0 ].size(); + + int dst_height = src_height * stride; + int dst_width = src_width * stride; + + // Allocate data for dst_alignment + dst_alignment.resize( dst_height, std::vector>( dst_width ) ); + + for ( int row_i = 0; row_i < src_height; row_i++ ) + { + for ( int col_i = 0; col_i < src_width; col_i++ ) + { + // Scale alignment + std::pair align_i = src_alignment[ row_i ][ col_i ]; + align_i.first *= scale_factor; + align_i.second *= scale_factor; + + // repeat + #pragma LOOP_UNROLL + for ( int stride_row_i = 0; stride_row_i < stride; ++stride_row_i ) + { + #pragma LOOP_UNROLL + for ( int stride_col_i = 0; stride_col_i < stride; ++stride_col_i ) + { + dst_alignment[ row_i + stride_row_i ][ col_i + stride_col_i ] = align_i; + } + } + } + } +} + + +static void align_image_level( \ + const cv::Mat& ref_img, \ + const cv::Mat& alt_img, \ + const std::pair& num_tiles, \ + std::vector>>& prev_aligement, \ + std::vector>>& alignment, \ + int inv_scale_factor, \ + int tile_size, \ + int prev_tile_size, \ + int search_radiou, \ + int distance ) +{ + + // Upsample pervious layer alignment + + // Coarsest level + // prev_alignment is invalid / empty, construct alignment as (0,0) + if ( prev_tile_size == -1 ) + { + alignment.resize( num_tiles.first, std::vector>( num_tiles.second, std::pair(0, 0) ) ); + } + // Upsample previous level alignment + else + { + if ( inv_scale_factor == 2 ) + { + upsample_alignment_stride<2>( prev_aligement, alignment, inv_scale_factor ); + } + else if ( inv_scale_factor == 4 ) + { + upsample_alignment_stride<4>( prev_aligement, alignment, inv_scale_factor ); + } + else + { + throw std::runtime_error("Invalid scale factor" + std::to_string( inv_scale_factor ) ); + } + } + + +} + void align::process( const hdrplus::burst& burst_images, \ - std::vector>>>& aligements ) + std::vector>>>& images_alignment ) { // Build image pyramid + + // image pyramid per image, per pyramid level std::vector> grayscale_images_pyramid; - grayscale_images_pyramid.resize( burst_images.grayscale_images_pad.size() ); - for ( int img_idx = 0; img_idx < burst_images.grayscale_images_pad.size(); ++img_idx ) + + grayscale_images_pyramid.resize( burst_images.num_images ); + for ( int img_idx = 0; img_idx < burst_images.num_images; ++img_idx ) { build_image_pyramid( grayscale_images_pyramid[ img_idx ], \ burst_images.grayscale_images_pad[ img_idx ], \ inv_scale_factors ); } + #ifndef NDEBUG + printf("%s::%s build image pyramid of size : ", __FILE__, __func__ ); + for ( int level_i = 0; level_i < num_levels; ++level_i ) + { + printf("(%d, %d) ", grayscale_images_pyramid[ 0 ][ level_i ].size().height, + grayscale_images_pyramid[ 0 ][ level_i ].size().width ); + } + printf("\n"); + #endif + + // number of tiles per pyramid level + // this is shared across all image at particular level + // `num_tiles_pyramid[0]` represent number of tiles in level 0 (finest level, original image) + std::vector> num_tiles_pyramid( num_levels ); + + for ( int level_i = 0; level_i < num_levels; ++level_i ) + { + cv::Size image_size_level_i = grayscale_images_pyramid[ 0 ][ level_i ].size(); + int half_tile_size_level_i = tile_sizes[ level_i ] / 2; + + num_tiles_pyramid[ level_i ] = std::make_pair( \ + image_size_level_i.height / half_tile_size_level_i - 1, \ + image_size_level_i.width / half_tile_size_level_i - 1 ); + } + + #ifndef NDEBUG + printf("%s::%s each pyramid tile size : ", __FILE__, __func__ ); + for ( int level_i = 0; level_i < num_levels; ++level_i ) + { + printf("(%d, %d) ", num_tiles_pyramid[ level_i ].first, + num_tiles_pyramid[ level_i ].second ); + } + printf("\n"); + #endif + + // Align every image + const std::vector& ref_imgs_pyramid = grayscale_images_pyramid[ burst_images.reference_image_idx ]; + for ( int img_idx = 0; img_idx < burst_images.num_images; ++img_idx ) + { + // Do not align with reference image + if ( img_idx == burst_images.reference_image_idx ) + continue; + + const std::vector& alt_imgs_pyramid = grayscale_images_pyramid[ img_idx ]; + + // Align every level from coarse to grain + // level 0 : finest level, the original image + // level 3 : coarsest level + std::vector> curr_alignment; + std::vector> prev_alignment; + for ( int level_i = num_levels - 1; level_i >= 0; level_i-- ) + { + align_image_level( + ref_imgs_pyramid[ level_i ], // reference image at current level + alt_imgs_pyramid[ level_i ], // alternative image at current level + num_tiles_pyramid[ level_i ], // number of tiles at this level + prev_alignment, // previous layer alignment + curr_alignment, // current layer alignment + inv_scale_factors[ level_i ], // ? + tile_sizes[ level_i ], // current level tile size + ( level_i == ( num_levels - 1 ) ? -1 : tile_sizes[ level_i + 1] ), // previous level tile size + search_radious[ level_i ], // search radious + distances[ level_i ] ); // L1/L2 distance + + // make curr alignment as previous alignment + prev_alignment.swap( curr_alignment ); + curr_alignment.clear(); + } // for pyramid level + + } // for alternative image }