From 2b550c2f63ebe553b61457488d0bb0bc0a324850 Mon Sep 17 00:00:00 2001 From: meshtag Date: Tue, 12 Jan 2021 17:33:01 +0530 Subject: [PATCH 01/23] Added all standard morphological transformations --- example/morphology.cpp | 175 +++++++++++++++++++++++++++++++++++++++++ example/original.png | Bin 0 -> 2082 bytes 2 files changed, 175 insertions(+) create mode 100644 example/morphology.cpp create mode 100644 example/original.png diff --git a/example/morphology.cpp b/example/morphology.cpp new file mode 100644 index 0000000000..27acd6d795 --- /dev/null +++ b/example/morphology.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include + +boost::gil::gray8_image_t img; + +//This function is used for applying basic threshold and convolution operations which are applied +//under the hood for all morphological transformations. +template +boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel const& ker_mat,const int threshold1,const int threshold2) +{ + using namespace boost::gil; + boost::gil::gray8_image_t intermediate_img(src_view.dimensions()); + threshold_binary(src_view, view(intermediate_img),threshold1, 255); + detail::convolve_2d(view(intermediate_img), ker_mat, view(intermediate_img)); + threshold_binary(const_view(intermediate_img),view(intermediate_img),threshold2,255); + return intermediate_img; +} + +template +boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) +{ + boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); + img_out_dilation = img; + for(int i=0;i +boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) +{ + boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); + img_out_erosion = img; + for(int i=0;i +boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat) +{ + boost::gil::gray8_image_t img_out_opening(src_view.dimensions()); + img_out_opening = erode(src_view,ker_mat,1); + return dilate(view(img_out_opening),ker_mat,1); +} + +template +boost::gil::gray8_image_t closing(SrcView const& src_view, Kernel const& ker_mat) +{ + boost::gil::gray8_image_t img_out_closing(src_view.dimensions()); + img_out_closing = dilate(src_view,ker_mat,1); + return erode(view(img_out_closing),ker_mat,1); +} + + +//This function calculates the difference between pixel values of first image_view and second +//image_view.This function can be used for rgb as well as grayscale images. +template +void difference_impl(SrcView const& src_view, DstView const& dst_view, DiffView const& diff_view) +{ + for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) + for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) + diff_view(view_col, view_row) = src_view(view_col,view_row) - dst_view(view_col,view_row); +} + + +template +void difference(SrcView const& src_view, DstView const& dst_view , DiffView const& diff_view) +{ + + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + + for (std::size_t i = 0; i < src_view.num_channels(); i++) + { + difference_impl( + nth_channel_view(src_view, i), + nth_channel_view(dst_view, i), + nth_channel_view(diff_view, i) + ); + } +} + +template +boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_dilate(src_view.dimensions()),int_erode(src_view.dimensions()),res_image(src_view.dimensions()); + int_dilate = dilate(src_view,ker_mat,1); + int_erode = erode(src_view,ker_mat,1); + difference(view(int_dilate),view(int_erode),view(res_image)); + return res_image; +} + +template +boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_opening(src_view.dimensions()),res_image(src_view.dimensions()); + int_opening = opening(src_view,ker_mat); + difference(src_view,view(int_opening),view(res_image)); + return res_image; +} + +template +boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_closing(src_view.dimensions()),res_image(src_view.dimensions()); + int_closing = closing(src_view,ker_mat); + difference(view(int_closing), src_view,view(res_image)); + return res_image; +} + +//Summary of applied morphological concepts +//Structuring element is SE = [0.1,0.1,0.1] +// |0.1,0.1,0.1| +// [0.1,0.1,0.1] +//SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the +//image to be convolved. +//1.Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, +//center pixel is made bright.We can vary the number of times dilation happens by varying the +//argument 'iterations' in the dilate function. +//2.Erosion:If 8 or more bright pixels coincide with the structuring element during convolution, +//center pixel is made bright.We can vary the number of times erosion happens by varying the +//argument 'iterations' in the erode function. +//3.Opening:Opening is just another name of erosion followed by dilation. +//It is useful in removing noise. +//4.Closing:Closing is reverse of Opening, Dilation followed by Erosion. +//It is useful in closing small holes inside the foreground objects, or small black points +//on the object. +//5.Morphological Gradient:It is the difference between dilation and erosion of an image. +//The result will look like the outline of the object. +//6.Top Hat:It is the difference between input image and Opening of the image. +//7.Black Hat:It is the difference between the closing of the input image and input image. + +//Functions have been made for applying above morphological transformations which +//return the transformed image. + +int main() +{ + using namespace boost::gil; + read_image("original.png", img, png_tag{}); + std::vectorker_vec(9,0.1f);//Structuring element + detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); + gray8_image_t img_out_dilation(img.dimensions()),img_out_erosion(img.dimensions()),img_out_opening(img.dimensions()); + gray8_image_t img_out_closing(img.dimensions()),img_out_mg(img.dimensions()),img_out_top_hat(img.dimensions()); + gray8_image_t img_out_black_hat(img.dimensions()); + //dilate(input_image_view,structuring_element,iterations) + img_out_dilation = dilate(view(img),ker_mat,1); + //erode(input_image_view,structuring_element,iterations) + img_out_erosion = erode(view(img),ker_mat,1); + //opening(input_image_view,structuring_element) + img_out_opening = opening(view(img),ker_mat); + //closing(input_image_view,structuring_element) + img_out_closing = closing(view(img),ker_mat); + //morphological_gradient(input_image_view,structuring_element) + img_out_mg = morphological_gradient(view(img),ker_mat); + //top_hat(input_image_view,structuring_element) + img_out_top_hat = top_hat(view(img),ker_mat); + //black_hat(input_image_view,structuring_element) + img_out_black_hat = black_hat(view(img),ker_mat); + + //Saving results of above morphological transformations. + write_view("out-morphological-example-dilation.png", view(img_out_dilation), png_tag{}); + write_view("out-morphological-example-erosion.png", view(img_out_erosion), png_tag{}); + write_view("out-morphological-example-opening.png", view(img_out_opening), png_tag{}); + write_view("out-morphological-example-closing.png", view(img_out_closing), png_tag{}); + write_view("out-morphological-example-morphological_gradient.png", view(img_out_mg), png_tag{}); + write_view("out-morphological-example-top_hat.png", view(img_out_top_hat), png_tag{}); + write_view("out-morphological-example-black_hat.png", view(img_out_black_hat), png_tag{}); +} diff --git a/example/original.png b/example/original.png new file mode 100644 index 0000000000000000000000000000000000000000..b63e99ed9da7b769fd4fe45826f1f5118fb24297 GIT binary patch literal 2082 zcmZ8iZB!Fi8U~6e6lgL-Es3HqNe0s5Za%OUTm@l-02eGGm}5C53=p(+p&(Y24~YWK zK(s~zJ5gHBi8NB>s42CDEv14Z@)2P@Srsd~k^)M*T0KZn5ZybR-Tv4gbMJkhdGEdN zecmtn{lpkA521&Xlap7hir5V2d+@7Y0tY9@y4#~pPKzgFi49wdobPnWkNh0%QaSqS zjxOi@^TQ>-X)lhwvGd>?_lTT(es>3(tFzub(NvAy zIzL_N=%N<~u4pLwB>>1_qlr^ZWhdM<)3v6ru8s!PTca3@j-!I7^6GIpP-Cc@kD( zks#cOtGc|>&2*QNl z-@N~ND`WIcdN{QDW0^FG2q~KzompwR+{E4haX$RR>1DIiIl0YWB(C z_K7Nv)~o{Z13a*r3WW%+5Lo54{A?l}lG1*4jQtiQ8u_K%O}9bf&VeyPaBVBRWQ}Qs zRcaUYQyZ?^ztSFH-i`lH+1sWat0O6k$XzgUB>fM9Ywvt#c=&Nxe;Q zW`6;35cEMMAkh`){i+D%Gf-Lt5(%7#o<5a)H5d=XP>OUcwFe3z| z#s^FUXoj)3*|Mn+_*c{U+VTgCs-?@5=ZDgyW1TXt3HUFSpCtNS%?;aAHk~@2XY(~E zb?Rr~%I3JEtJ`P@dCAD=2_RC*sNSX!^z0zGmYLX2K*BgpbTszJ5|9vZQ2JO3I_5mA9KC5A zgw#;RNyoMLQ;NB zuIpv0R2`-J=4=?w6WDnJ^bvQWhrlk(&5n0-5K6yE)k7$R{kyFWDywzyWnvKDGT;jB zA{Uvnq2IQV|9mw4yu10A$t}u@1SflOIQ&177yja<=p+}nuK}^pB1sJhvjm(4G2q$w zsr3EYEf>OXddj@=v%^R~EF+qha?~~oB&pbDZN+efAm9dEFvC>>JXNI7Qp42(?1rdd zVx84*>I=1@ZT9A?m-CCaS~l(MdrGgS&xF5vazET1ruZ=TN?jkdn?AFDekOeXKVV0F u^w54g2gVt>+wmdSAYM%We=~M;&9AKA^Y#TxX!BmK`NwWdB+f=^4*UmVeU2^w literal 0 HcmV?d00001 From 48436fc82ab3d6ffbd2ce13d343640edc54ad02b Mon Sep 17 00:00:00 2001 From: meshtag Date: Thu, 14 Jan 2021 21:49:53 +0530 Subject: [PATCH 02/23] Improved comments and some other things --- example/morphology.cpp | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/example/morphology.cpp b/example/morphology.cpp index 27acd6d795..be27ee52d4 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -5,9 +5,8 @@ #include #include -boost::gil::gray8_image_t img; -//This function is used for applying basic threshold and convolution operations which are applied +//This function is used for applying basic thresholding and convolution operations which are applied //under the hood for all morphological transformations. template boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel const& ker_mat,const int threshold1,const int threshold2) @@ -20,26 +19,34 @@ boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel cons return intermediate_img; } +// Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, +// center pixel is made bright.We can vary the number of times dilation happens by varying the +// argument 'iterations' in the dilate function. template boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) { boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); - img_out_dilation = img; + boost::gil::transform_pixels(src_view,view(img_out_dilation),[](const boost::gil::gray8_pixel_t& p){return p[0];}); for(int i=0;i boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) { boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); - img_out_erosion = img; + boost::gil::transform_pixels(src_view,view(img_out_erosion),[](const boost::gil::gray8_pixel_t& p){return p[0];}); for(int i=0;i boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat) { @@ -48,6 +55,9 @@ boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat return dilate(view(img_out_opening),ker_mat,1); } +//Closing:Closing is reverse of Opening, Dilation followed by Erosion. +//It is useful in closing small holes inside the foreground objects, or small black points +//on the object. template boost::gil::gray8_image_t closing(SrcView const& src_view, Kernel const& ker_mat) { @@ -84,6 +94,8 @@ void difference(SrcView const& src_view, DstView const& dst_view , DiffView cons } } +//Morphological Gradient:It is the difference between dilation and erosion of an image. +//The result will look like the outline of the object. template boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel const& ker_mat) { @@ -95,6 +107,7 @@ boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel return res_image; } +//Top Hat:It is the difference between input image and Opening of the image. template boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat) { @@ -105,6 +118,7 @@ boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat return res_image; } +//Black Hat:It is the difference between the closing of the input image and input image. template boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_mat) { @@ -115,34 +129,16 @@ boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_m return res_image; } -//Summary of applied morphological concepts //Structuring element is SE = [0.1,0.1,0.1] // |0.1,0.1,0.1| // [0.1,0.1,0.1] //SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the //image to be convolved. -//1.Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, -//center pixel is made bright.We can vary the number of times dilation happens by varying the -//argument 'iterations' in the dilate function. -//2.Erosion:If 8 or more bright pixels coincide with the structuring element during convolution, -//center pixel is made bright.We can vary the number of times erosion happens by varying the -//argument 'iterations' in the erode function. -//3.Opening:Opening is just another name of erosion followed by dilation. -//It is useful in removing noise. -//4.Closing:Closing is reverse of Opening, Dilation followed by Erosion. -//It is useful in closing small holes inside the foreground objects, or small black points -//on the object. -//5.Morphological Gradient:It is the difference between dilation and erosion of an image. -//The result will look like the outline of the object. -//6.Top Hat:It is the difference between input image and Opening of the image. -//7.Black Hat:It is the difference between the closing of the input image and input image. - -//Functions have been made for applying above morphological transformations which -//return the transformed image. int main() { using namespace boost::gil; + boost::gil::gray8_image_t img; read_image("original.png", img, png_tag{}); std::vectorker_vec(9,0.1f);//Structuring element detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); From 14900be7116e0002090569a637d4d52928bc0cea Mon Sep 17 00:00:00 2001 From: meshtag Date: Sun, 17 Jan 2021 12:58:55 +0530 Subject: [PATCH 03/23] Applied adviced changes --- example/morphology.cpp | 135 +----------------- .../boost/gil/image_processing/morphology.hpp | 131 +++++++++++++++++ 2 files changed, 135 insertions(+), 131 deletions(-) create mode 100644 include/boost/gil/image_processing/morphology.hpp diff --git a/example/morphology.cpp b/example/morphology.cpp index be27ee52d4..15c4d158a9 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -1,137 +1,10 @@ -#include -#include #include +#include #include -#include -#include - -//This function is used for applying basic thresholding and convolution operations which are applied -//under the hood for all morphological transformations. -template -boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel const& ker_mat,const int threshold1,const int threshold2) -{ - using namespace boost::gil; - boost::gil::gray8_image_t intermediate_img(src_view.dimensions()); - threshold_binary(src_view, view(intermediate_img),threshold1, 255); - detail::convolve_2d(view(intermediate_img), ker_mat, view(intermediate_img)); - threshold_binary(const_view(intermediate_img),view(intermediate_img),threshold2,255); - return intermediate_img; -} - -// Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, -// center pixel is made bright.We can vary the number of times dilation happens by varying the -// argument 'iterations' in the dilate function. -template -boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) -{ - boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); - boost::gil::transform_pixels(src_view,view(img_out_dilation),[](const boost::gil::gray8_pixel_t& p){return p[0];}); - for(int i=0;i -boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) -{ - boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); - boost::gil::transform_pixels(src_view,view(img_out_erosion),[](const boost::gil::gray8_pixel_t& p){return p[0];}); - for(int i=0;i -boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat) -{ - boost::gil::gray8_image_t img_out_opening(src_view.dimensions()); - img_out_opening = erode(src_view,ker_mat,1); - return dilate(view(img_out_opening),ker_mat,1); -} - -//Closing:Closing is reverse of Opening, Dilation followed by Erosion. -//It is useful in closing small holes inside the foreground objects, or small black points -//on the object. -template -boost::gil::gray8_image_t closing(SrcView const& src_view, Kernel const& ker_mat) -{ - boost::gil::gray8_image_t img_out_closing(src_view.dimensions()); - img_out_closing = dilate(src_view,ker_mat,1); - return erode(view(img_out_closing),ker_mat,1); -} - - -//This function calculates the difference between pixel values of first image_view and second -//image_view.This function can be used for rgb as well as grayscale images. -template -void difference_impl(SrcView const& src_view, DstView const& dst_view, DiffView const& diff_view) -{ - for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) - for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) - diff_view(view_col, view_row) = src_view(view_col,view_row) - dst_view(view_col,view_row); -} - - -template -void difference(SrcView const& src_view, DstView const& dst_view , DiffView const& diff_view) -{ - - BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - - for (std::size_t i = 0; i < src_view.num_channels(); i++) - { - difference_impl( - nth_channel_view(src_view, i), - nth_channel_view(dst_view, i), - nth_channel_view(diff_view, i) - ); - } -} - -//Morphological Gradient:It is the difference between dilation and erosion of an image. -//The result will look like the outline of the object. -template -boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_dilate(src_view.dimensions()),int_erode(src_view.dimensions()),res_image(src_view.dimensions()); - int_dilate = dilate(src_view,ker_mat,1); - int_erode = erode(src_view,ker_mat,1); - difference(view(int_dilate),view(int_erode),view(res_image)); - return res_image; -} - -//Top Hat:It is the difference between input image and Opening of the image. -template -boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_opening(src_view.dimensions()),res_image(src_view.dimensions()); - int_opening = opening(src_view,ker_mat); - difference(src_view,view(int_opening),view(res_image)); - return res_image; -} - -//Black Hat:It is the difference between the closing of the input image and input image. -template -boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_closing(src_view.dimensions()),res_image(src_view.dimensions()); - int_closing = closing(src_view,ker_mat); - difference(view(int_closing), src_view,view(res_image)); - return res_image; -} - -//Structuring element is SE = [0.1,0.1,0.1] -// |0.1,0.1,0.1| -// [0.1,0.1,0.1] +//current structuring element is SE = [0.1,0.1,0.1] +// |0.1,0.1,0.1| +// [0.1,0.1,0.1] //SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the //image to be convolved. diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp new file mode 100644 index 0000000000..ee29f9f8d0 --- /dev/null +++ b/include/boost/gil/image_processing/morphology.hpp @@ -0,0 +1,131 @@ +#ifndef BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP +#define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP + +#include +#include +#include + + +//This function is used for applying basic thresholding and convolution operations which are applied +//under the hood for all morphological transformations. +template +boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel const& ker_mat,const int threshold1,const int threshold2) +{ + using namespace boost::gil; + boost::gil::gray8_image_t intermediate_img(src_view.dimensions()); + threshold_binary(src_view, view(intermediate_img),threshold1, 255); + detail::convolve_2d(view(intermediate_img), ker_mat, view(intermediate_img)); + threshold_binary(const_view(intermediate_img),view(intermediate_img),threshold2,255); + return intermediate_img; +} + +// Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, +// center pixel is made bright.We can vary the number of times dilation happens by varying the +// argument 'iterations' in the dilate function. +template +boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) +{ + boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); + boost::gil::transform_pixels(src_view,view(img_out_dilation),[](const boost::gil::gray8_pixel_t& p){return p[0];}); + for(int i=0;i +boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) +{ + boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); + boost::gil::transform_pixels(src_view,view(img_out_erosion),[](const boost::gil::gray8_pixel_t& p){return p[0];}); + for(int i=0;i +boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat) +{ + boost::gil::gray8_image_t img_out_opening(src_view.dimensions()); + img_out_opening = erode(src_view,ker_mat,1); + return dilate(view(img_out_opening),ker_mat,1); +} + +//Closing:Closing is reverse of Opening, Dilation followed by Erosion. +//It is useful in closing small holes inside the foreground objects, or small black points +//on the object. +template +boost::gil::gray8_image_t closing(SrcView const& src_view, Kernel const& ker_mat) +{ + boost::gil::gray8_image_t img_out_closing(src_view.dimensions()); + img_out_closing = dilate(src_view,ker_mat,1); + return erode(view(img_out_closing),ker_mat,1); +} + + +//This function calculates the difference between pixel values of first image_view and second +//image_view.This function can be used for rgb as well as grayscale images. +template +void difference_impl(SrcView const& src_view, DstView const& dst_view, DiffView const& diff_view) +{ + for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) + for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) + diff_view(view_col, view_row) = src_view(view_col,view_row) - dst_view(view_col,view_row); +} + + +template +void difference(SrcView const& src_view, DstView const& dst_view , DiffView const& diff_view) +{ + + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + + for (std::size_t i = 0; i < src_view.num_channels(); i++) + { + difference_impl( + nth_channel_view(src_view, i), + nth_channel_view(dst_view, i), + nth_channel_view(diff_view, i) + ); + } +} + +//Morphological Gradient:It is the difference between dilation and erosion of an image. +//The result will look like the outline of the object. +template +boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_dilate(src_view.dimensions()),int_erode(src_view.dimensions()),res_image(src_view.dimensions()); + int_dilate = dilate(src_view,ker_mat,1); + int_erode = erode(src_view,ker_mat,1); + difference(view(int_dilate),view(int_erode),view(res_image)); + return res_image; +} + +//Top Hat:It is the difference between input image and Opening of the image. +template +boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_opening(src_view.dimensions()),res_image(src_view.dimensions()); + int_opening = opening(src_view,ker_mat); + difference(src_view,view(int_opening),view(res_image)); + return res_image; +} + +//Black Hat:It is the difference between the closing of the input image and input image. +template +boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gray8_image_t int_closing(src_view.dimensions()),res_image(src_view.dimensions()); + int_closing = closing(src_view,ker_mat); + difference(view(int_closing), src_view,view(res_image)); + return res_image; +} +#endif //BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP \ No newline at end of file From 53d92b28451757a05a5043cdac1e9e96b13b3740 Mon Sep 17 00:00:00 2001 From: meshtag Date: Sun, 17 Jan 2021 13:00:00 +0530 Subject: [PATCH 04/23] Applied adviced changes --- example/morphology.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/morphology.cpp b/example/morphology.cpp index 15c4d158a9..304e409147 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -6,7 +6,7 @@ // |0.1,0.1,0.1| // [0.1,0.1,0.1] //SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the -//image to be convolved. +//image to be convolved. The structuring element can be easily changed by the user. int main() { From 6ac3102cec60370f6e7ce590bacf0d1a591f3010 Mon Sep 17 00:00:00 2001 From: meshtag Date: Wed, 20 Jan 2021 17:05:51 +0530 Subject: [PATCH 05/23] Should handle grayscale dilation/erosion --- example/morphology.cpp | 14 ++-- .../boost/gil/image_processing/morphology.hpp | 70 +++++++++++++++---- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/example/morphology.cpp b/example/morphology.cpp index 304e409147..dcd0a17030 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -2,9 +2,9 @@ #include #include -//current structuring element is SE = [0.1,0.1,0.1] -// |0.1,0.1,0.1| -// [0.1,0.1,0.1] +//Default structuring element is SE = [1,1,1] +// |1,1,1| +// [1,1,1] //SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the //image to be convolved. The structuring element can be easily changed by the user. @@ -13,7 +13,13 @@ int main() using namespace boost::gil; boost::gil::gray8_image_t img; read_image("original.png", img, png_tag{}); - std::vectorker_vec(9,0.1f);//Structuring element + + //Image can be converted to a binary format with high value as 255 and low value as 0 + //by uncommenting the following threshold operator.This can be used for binary morphological + //operations. + threshold_binary(view(img), view(img),170, 255); + + std::vectorker_vec(9,1.0f);//Structuring element detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); gray8_image_t img_out_dilation(img.dimensions()),img_out_erosion(img.dimensions()),img_out_opening(img.dimensions()); gray8_image_t img_out_closing(img.dimensions()),img_out_mg(img.dimensions()),img_out_top_hat(img.dimensions()); diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp index ee29f9f8d0..2bd1c20c2e 100644 --- a/include/boost/gil/image_processing/morphology.hpp +++ b/include/boost/gil/image_processing/morphology.hpp @@ -1,26 +1,68 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP #define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP -#include #include -#include +template +void morph_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel,const int identifier) +{ + int flip_ker_row, flip_ker_col, row_boundary, col_boundary; + int max_overlapped_element,min_overlapped_element; + for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) + { + for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) + { + max_overlapped_element = 0,min_overlapped_element = 256; + for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) + { + flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel + + for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) + { + flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel + + // index of input signal, used for checking boundary + row_boundary = view_row + (kernel.center_y() - flip_ker_row); + col_boundary = view_col + (kernel.center_x() - flip_ker_col); + + // ignore input samples which are out of bound + if (row_boundary >= 0 && row_boundary < src_view.height() && + col_boundary >= 0 && col_boundary < src_view.width()) + { + if(src_view(col_boundary, row_boundary) > max_overlapped_element) + max_overlapped_element = src_view(col_boundary, row_boundary); + if(src_view(col_boundary, row_boundary) < min_overlapped_element) + min_overlapped_element = src_view(col_boundary, row_boundary); + } + } + } + if(identifier)//identifier = 1 for dilation + dst_view(view_col, view_row) = max_overlapped_element; + else //identifier = 0 for erosion + dst_view(view_col, view_row) = min_overlapped_element; + } + } +} -//This function is used for applying basic thresholding and convolution operations which are applied -//under the hood for all morphological transformations. template -boost::gil::gray8_image_t thresh_con_thresh(SrcView const& src_view, Kernel const& ker_mat,const int threshold1,const int threshold2) +boost::gil::gray8_image_t morph(SrcView const& src_view, Kernel const& ker_mat,const int identifier) { using namespace boost::gil; + BOOST_ASSERT(ker_mat.size() != 0); boost::gil::gray8_image_t intermediate_img(src_view.dimensions()); - threshold_binary(src_view, view(intermediate_img),threshold1, 255); - detail::convolve_2d(view(intermediate_img), ker_mat, view(intermediate_img)); - threshold_binary(const_view(intermediate_img),view(intermediate_img),threshold2,255); + for (std::size_t i = 0; i < src_view.num_channels(); i++) + { + morph_impl( + nth_channel_view(src_view, i), + nth_channel_view(view(intermediate_img), i), + ker_mat,identifier + ); + } return intermediate_img; } -// Dilation:If 1 or more bright pixels coincide with the structuring element during convolution, -// center pixel is made bright.We can vary the number of times dilation happens by varying the +// Dilation:Give the maximum overlapped value to the pixel overlapping with the center element of +//structuring element.We can vary the number of times dilation happens by varying the // argument 'iterations' in the dilate function. template boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) @@ -28,12 +70,12 @@ boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat, boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); boost::gil::transform_pixels(src_view,view(img_out_dilation),[](const boost::gil::gray8_pixel_t& p){return p[0];}); for(int i=0;i boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) @@ -41,7 +83,7 @@ boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,c boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); boost::gil::transform_pixels(src_view,view(img_out_erosion),[](const boost::gil::gray8_pixel_t& p){return p[0];}); for(int i=0;i Date: Fri, 22 Jan 2021 00:58:18 +0530 Subject: [PATCH 06/23] Checking --- example/morphology.cpp | 50 ----- example/original.png | Bin 2082 -> 0 bytes .../boost/gil/image_processing/morphology.hpp | 173 ------------------ 3 files changed, 223 deletions(-) delete mode 100644 example/morphology.cpp delete mode 100644 example/original.png delete mode 100644 include/boost/gil/image_processing/morphology.hpp diff --git a/example/morphology.cpp b/example/morphology.cpp deleted file mode 100644 index dcd0a17030..0000000000 --- a/example/morphology.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -//Default structuring element is SE = [1,1,1] -// |1,1,1| -// [1,1,1] -//SE(1,1)(center pixel) is the one which coincides with the currently considered pixel of the -//image to be convolved. The structuring element can be easily changed by the user. - -int main() -{ - using namespace boost::gil; - boost::gil::gray8_image_t img; - read_image("original.png", img, png_tag{}); - - //Image can be converted to a binary format with high value as 255 and low value as 0 - //by uncommenting the following threshold operator.This can be used for binary morphological - //operations. - threshold_binary(view(img), view(img),170, 255); - - std::vectorker_vec(9,1.0f);//Structuring element - detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); - gray8_image_t img_out_dilation(img.dimensions()),img_out_erosion(img.dimensions()),img_out_opening(img.dimensions()); - gray8_image_t img_out_closing(img.dimensions()),img_out_mg(img.dimensions()),img_out_top_hat(img.dimensions()); - gray8_image_t img_out_black_hat(img.dimensions()); - //dilate(input_image_view,structuring_element,iterations) - img_out_dilation = dilate(view(img),ker_mat,1); - //erode(input_image_view,structuring_element,iterations) - img_out_erosion = erode(view(img),ker_mat,1); - //opening(input_image_view,structuring_element) - img_out_opening = opening(view(img),ker_mat); - //closing(input_image_view,structuring_element) - img_out_closing = closing(view(img),ker_mat); - //morphological_gradient(input_image_view,structuring_element) - img_out_mg = morphological_gradient(view(img),ker_mat); - //top_hat(input_image_view,structuring_element) - img_out_top_hat = top_hat(view(img),ker_mat); - //black_hat(input_image_view,structuring_element) - img_out_black_hat = black_hat(view(img),ker_mat); - - //Saving results of above morphological transformations. - write_view("out-morphological-example-dilation.png", view(img_out_dilation), png_tag{}); - write_view("out-morphological-example-erosion.png", view(img_out_erosion), png_tag{}); - write_view("out-morphological-example-opening.png", view(img_out_opening), png_tag{}); - write_view("out-morphological-example-closing.png", view(img_out_closing), png_tag{}); - write_view("out-morphological-example-morphological_gradient.png", view(img_out_mg), png_tag{}); - write_view("out-morphological-example-top_hat.png", view(img_out_top_hat), png_tag{}); - write_view("out-morphological-example-black_hat.png", view(img_out_black_hat), png_tag{}); -} diff --git a/example/original.png b/example/original.png deleted file mode 100644 index b63e99ed9da7b769fd4fe45826f1f5118fb24297..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2082 zcmZ8iZB!Fi8U~6e6lgL-Es3HqNe0s5Za%OUTm@l-02eGGm}5C53=p(+p&(Y24~YWK zK(s~zJ5gHBi8NB>s42CDEv14Z@)2P@Srsd~k^)M*T0KZn5ZybR-Tv4gbMJkhdGEdN zecmtn{lpkA521&Xlap7hir5V2d+@7Y0tY9@y4#~pPKzgFi49wdobPnWkNh0%QaSqS zjxOi@^TQ>-X)lhwvGd>?_lTT(es>3(tFzub(NvAy zIzL_N=%N<~u4pLwB>>1_qlr^ZWhdM<)3v6ru8s!PTca3@j-!I7^6GIpP-Cc@kD( zks#cOtGc|>&2*QNl z-@N~ND`WIcdN{QDW0^FG2q~KzompwR+{E4haX$RR>1DIiIl0YWB(C z_K7Nv)~o{Z13a*r3WW%+5Lo54{A?l}lG1*4jQtiQ8u_K%O}9bf&VeyPaBVBRWQ}Qs zRcaUYQyZ?^ztSFH-i`lH+1sWat0O6k$XzgUB>fM9Ywvt#c=&Nxe;Q zW`6;35cEMMAkh`){i+D%Gf-Lt5(%7#o<5a)H5d=XP>OUcwFe3z| z#s^FUXoj)3*|Mn+_*c{U+VTgCs-?@5=ZDgyW1TXt3HUFSpCtNS%?;aAHk~@2XY(~E zb?Rr~%I3JEtJ`P@dCAD=2_RC*sNSX!^z0zGmYLX2K*BgpbTszJ5|9vZQ2JO3I_5mA9KC5A zgw#;RNyoMLQ;NB zuIpv0R2`-J=4=?w6WDnJ^bvQWhrlk(&5n0-5K6yE)k7$R{kyFWDywzyWnvKDGT;jB zA{Uvnq2IQV|9mw4yu10A$t}u@1SflOIQ&177yja<=p+}nuK}^pB1sJhvjm(4G2q$w zsr3EYEf>OXddj@=v%^R~EF+qha?~~oB&pbDZN+efAm9dEFvC>>JXNI7Qp42(?1rdd zVx84*>I=1@ZT9A?m-CCaS~l(MdrGgS&xF5vazET1ruZ=TN?jkdn?AFDekOeXKVV0F u^w54g2gVt>+wmdSAYM%We=~M;&9AKA^Y#TxX!BmK`NwWdB+f=^4*UmVeU2^w diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp deleted file mode 100644 index 2bd1c20c2e..0000000000 --- a/include/boost/gil/image_processing/morphology.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP -#define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP - -#include - -template -void morph_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel,const int identifier) -{ - int flip_ker_row, flip_ker_col, row_boundary, col_boundary; - int max_overlapped_element,min_overlapped_element; - for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) - { - for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) - { - max_overlapped_element = 0,min_overlapped_element = 256; - for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) - { - flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel - - for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) - { - flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel - - // index of input signal, used for checking boundary - row_boundary = view_row + (kernel.center_y() - flip_ker_row); - col_boundary = view_col + (kernel.center_x() - flip_ker_col); - - // ignore input samples which are out of bound - if (row_boundary >= 0 && row_boundary < src_view.height() && - col_boundary >= 0 && col_boundary < src_view.width()) - { - if(src_view(col_boundary, row_boundary) > max_overlapped_element) - max_overlapped_element = src_view(col_boundary, row_boundary); - if(src_view(col_boundary, row_boundary) < min_overlapped_element) - min_overlapped_element = src_view(col_boundary, row_boundary); - } - } - } - if(identifier)//identifier = 1 for dilation - dst_view(view_col, view_row) = max_overlapped_element; - else //identifier = 0 for erosion - dst_view(view_col, view_row) = min_overlapped_element; - } - } -} - -template -boost::gil::gray8_image_t morph(SrcView const& src_view, Kernel const& ker_mat,const int identifier) -{ - using namespace boost::gil; - BOOST_ASSERT(ker_mat.size() != 0); - boost::gil::gray8_image_t intermediate_img(src_view.dimensions()); - for (std::size_t i = 0; i < src_view.num_channels(); i++) - { - morph_impl( - nth_channel_view(src_view, i), - nth_channel_view(view(intermediate_img), i), - ker_mat,identifier - ); - } - return intermediate_img; -} - -// Dilation:Give the maximum overlapped value to the pixel overlapping with the center element of -//structuring element.We can vary the number of times dilation happens by varying the -// argument 'iterations' in the dilate function. -template -boost::gil::gray8_image_t dilate(SrcView const& src_view, Kernel const& ker_mat,const int iterations) -{ - boost::gil::gray8_image_t img_out_dilation(src_view.dimensions()); - boost::gil::transform_pixels(src_view,view(img_out_dilation),[](const boost::gil::gray8_pixel_t& p){return p[0];}); - for(int i=0;i -boost::gil::gray8_image_t erode(SrcView const& src_view, Kernel const& ker_mat,const int iterations) -{ - boost::gil::gray8_image_t img_out_erosion(src_view.dimensions()); - boost::gil::transform_pixels(src_view,view(img_out_erosion),[](const boost::gil::gray8_pixel_t& p){return p[0];}); - for(int i=0;i -boost::gil::gray8_image_t opening(SrcView const& src_view, Kernel const& ker_mat) -{ - boost::gil::gray8_image_t img_out_opening(src_view.dimensions()); - img_out_opening = erode(src_view,ker_mat,1); - return dilate(view(img_out_opening),ker_mat,1); -} - -//Closing:Closing is reverse of Opening, Dilation followed by Erosion. -//It is useful in closing small holes inside the foreground objects, or small black points -//on the object. -template -boost::gil::gray8_image_t closing(SrcView const& src_view, Kernel const& ker_mat) -{ - boost::gil::gray8_image_t img_out_closing(src_view.dimensions()); - img_out_closing = dilate(src_view,ker_mat,1); - return erode(view(img_out_closing),ker_mat,1); -} - - -//This function calculates the difference between pixel values of first image_view and second -//image_view.This function can be used for rgb as well as grayscale images. -template -void difference_impl(SrcView const& src_view, DstView const& dst_view, DiffView const& diff_view) -{ - for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) - for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) - diff_view(view_col, view_row) = src_view(view_col,view_row) - dst_view(view_col,view_row); -} - - -template -void difference(SrcView const& src_view, DstView const& dst_view , DiffView const& diff_view) -{ - - BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - - for (std::size_t i = 0; i < src_view.num_channels(); i++) - { - difference_impl( - nth_channel_view(src_view, i), - nth_channel_view(dst_view, i), - nth_channel_view(diff_view, i) - ); - } -} - -//Morphological Gradient:It is the difference between dilation and erosion of an image. -//The result will look like the outline of the object. -template -boost::gil::gray8_image_t morphological_gradient(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_dilate(src_view.dimensions()),int_erode(src_view.dimensions()),res_image(src_view.dimensions()); - int_dilate = dilate(src_view,ker_mat,1); - int_erode = erode(src_view,ker_mat,1); - difference(view(int_dilate),view(int_erode),view(res_image)); - return res_image; -} - -//Top Hat:It is the difference between input image and Opening of the image. -template -boost::gil::gray8_image_t top_hat(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_opening(src_view.dimensions()),res_image(src_view.dimensions()); - int_opening = opening(src_view,ker_mat); - difference(src_view,view(int_opening),view(res_image)); - return res_image; -} - -//Black Hat:It is the difference between the closing of the input image and input image. -template -boost::gil::gray8_image_t black_hat(SrcView const& src_view, Kernel const& ker_mat) -{ - using namespace boost::gil; - gray8_image_t int_closing(src_view.dimensions()),res_image(src_view.dimensions()); - int_closing = closing(src_view,ker_mat); - difference(view(int_closing), src_view,view(res_image)); - return res_image; -} -#endif //BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP \ No newline at end of file From 9ad646ed850dc4907bd82e6e13dcb53f79742cab Mon Sep 17 00:00:00 2001 From: meshtag Date: Fri, 19 Feb 2021 20:43:34 +0530 Subject: [PATCH 07/23] Add higher order kernel support for sobel operator --- include/boost/gil/detail/math.hpp | 126 ++++++++++ .../boost/gil/image_processing/numeric.hpp | 76 +++--- test/core/image_processing/sobel_scharr.cpp | 218 ++++++++++++++++++ 3 files changed, 391 insertions(+), 29 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 934f11ac82..ccf715c90b 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Prathamesh Tagore // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,15 +11,29 @@ #include #include +#include namespace boost { namespace gil { namespace detail { +enum class kernel_type +{ + sobeldx, + sobeldy, +}; + static constexpr double pi = 3.14159265358979323846; static constexpr std::array dx_sobel = {{-1, 0, 1, -2, 0, 2, -1, 0, 1}}; +static constexpr std::array dx_sobel2 = {{ + -1,-2,0,2,1,-4,-8,0,8,4,-6,-12,0,12,6,-4,-8,0,8,4,-1,-2,0,2,1 +}}; static constexpr std::array dx_scharr = {{-1, 0, 1, -1, 0, 1, -1, 0, 1}}; static constexpr std::array dy_sobel = {{1, 2, 1, 0, 0, 0, -1, -2, -1}}; +static constexpr std::array dy_sobel2 = {{ + -1,-4,-6,-4,-1,-2,-8,-12,-8,-2,0,0,0,0,0,2,8,12,8,2,1,4,6,4,1 +}}; static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; +static std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; template inline detail::kernel_2d get_identity_kernel() @@ -28,6 +43,117 @@ inline detail::kernel_2d get_identity_kernel() return kernel; } +// Please refer https://stackoverflow.com/a/10032882/14958679 for getting an overview of the +// concept applied for obtaining higher order sobel kernels. + +/// \brief Produces higher order kernel vector by performing discrete convolution between lower +/// order kernel vector and a smoothing kernel vector(kernel used for suppressing noise). +/// \ingroup ImageProcessingMath +/// \param kernel1 - First argument for kernel vector convolution. +/// \param kernel2 - Second argument for kernel vector convolution. +/// \tparam T1 Type of first argument for kernel vector convolution. +/// \tparam T2 Type of second argument for kernel vector convolution. +template +static std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) +{ + size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; + std::vector> convolved_kernel(convolved_kernel_size, + std::vector(convolved_kernel_size)), + dummy_kernel(convolved_kernel_size, + std::vector(convolved_kernel_size)); + + // 'dummy_kernel' will be made by padding 'kernel1' with appropriate no. of rows and columns + // containing zeros to match the size specified by 'convolved_kernel_size'. + // 'convolved kernel' will store the result obtained after applying convolution between + // 'dummy_kernel' and 'kernel2'. + // 'padding_reference' will be used to apply appropriate padding with zeros around 'kernel1' + // to create 'dummy_kernel'. + + std::ptrdiff_t padding_reference = (kernel2.size() - 1) / 2; + for(std::ptrdiff_t row = padding_reference;row < dummy_kernel.size() - padding_reference; ++row) + for(std::ptrdiff_t col = padding_reference;col < dummy_kernel.size() - padding_reference; + ++col) + { + dummy_kernel[row][col] = kernel1[row - padding_reference][col - padding_reference]; + } + + std::ptrdiff_t flip_ker_row, flip_ker_col, row_boundary, col_boundary; + float aux_total; + for(std::ptrdiff_t dummy_row = 0;dummy_row < convolved_kernel_size; ++dummy_row) + { + for(std::ptrdiff_t dummy_col = 0;dummy_col < convolved_kernel_size; ++dummy_col) + { + aux_total = 0.0f; + for(std::ptrdiff_t ker2_row = 0;ker2_row < kernel2.size(); ++ker2_row) + { + flip_ker_row = kernel2.size() - 1 - ker2_row; + for(std::ptrdiff_t ker2_col = 0;ker2_col < kernel2.size(); ++ker2_col) + { + flip_ker_col = kernel2.size() - 1 - ker2_col; + row_boundary = dummy_row + kernel2.size()/2 - flip_ker_row; + col_boundary = dummy_col + kernel2.size()/2 - flip_ker_col; + + // ignore input samples which are out of bound + if (row_boundary >= 0 && row_boundary < convolved_kernel_size && + col_boundary >= 0 && col_boundary < convolved_kernel_size) + { + aux_total += + kernel2[flip_ker_row][flip_ker_col] * + dummy_kernel[row_boundary][col_boundary]; + } + } + } + convolved_kernel[dummy_row][dummy_col] = aux_total; + } + } + return convolved_kernel; +} + +/// \brief Fills kernel vector given as argument with a second order kernel in horizontal or +/// vertical direction. The type of the kernel which is to be used for filling will be indicated +/// by the variable 'type'. +/// \ingroup ImageProcessingMath +/// \param kernel - kernel vector which will be filled. +/// \param type - Indicates the type of second order derivative kernel which is to be filled inside +/// first argument. +void kernel_fill(std::vector>& kernel, kernel_type type) +{ + if(type == kernel_type::sobeldx) + { + for(std::ptrdiff_t row = 0;row < 5; ++row) + for(std::ptrdiff_t col = 0;col < 5; ++col) + kernel[row][col] = dx_sobel2[5 * row + col]; + } + else if(type == kernel_type::sobeldy) + { + for(std::ptrdiff_t row =0;row < 5; ++row) + for(std::ptrdiff_t col = 0;col < 5; ++col) + kernel[row][col] = dy_sobel2[5 * row + col]; + } +} + +/// \brief Passes parameters to 'kernel_convolve_impl()' repeatedly until kernel vector of desired +/// order is obtained. +/// \ingroup ImageProcessingMath +/// \param order - Indicates the order of kernel vector which is to be returned. +/// \param type - Indicates the type of kernel vector which is to be returned. +std::vector kernel_convolve(unsigned int order, kernel_type type) +{ + std::vector convolved_kernel_flatten; + std::vector> convolved_kernel(5, std::vector(5)); + + kernel_fill(convolved_kernel, type); + + for(unsigned int i = 0;i < order - 2; ++i) + convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); + + for(std::ptrdiff_t row = 0;row < convolved_kernel.size(); ++row) + for(std::ptrdiff_t col = 0;col < convolved_kernel.size(); ++col) + convolved_kernel_flatten.push_back(convolved_kernel[row][col]); + + return convolved_kernel_flatten; +} + }}} // namespace boost::gil::detail #endif diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index b601b17dd0..ba1b89cdee 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Prathamesh Tagore // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -16,6 +17,7 @@ // fixes ambigious call to std::abs, https://stackoverflow.com/a/30084734/4593721 #include #include +#include namespace boost { namespace gil { @@ -48,7 +50,7 @@ inline double lanczos(double x, std::ptrdiff_t a) if (0 <= x && x <= 0) return 1; - if (static_cast(-a) < x && x < static_cast(a)) + if (-a < x && x < a) return normalized_sinc(x) / normalized_sinc(x / static_cast(a)); return 0; @@ -161,22 +163,30 @@ inline detail::kernel_2d generate_gaussian_kernel(std::size_t side template > inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1) { - switch (degree) + if(degree == 0) + return detail::get_identity_kernel(); + else if (degree == 1) { - case 0: - { - return detail::get_identity_kernel(); - } - case 1: - { - detail::kernel_2d result(3, 1, 1); - std::copy(detail::dx_sobel.begin(), detail::dx_sobel.end(), result.begin()); - return result; - } - default: - throw std::logic_error("not supported yet"); + detail::kernel_2d result(3, 1, 1); + std::copy(detail::dx_sobel.begin(), detail::dx_sobel.end(), result.begin()); + return result; } - + else if(degree == 2) + { + detail::kernel_2d result(5, 2, 2); + std::copy(detail::dx_sobel2.begin(), detail::dx_sobel2.end(), result.begin()); + return result; + } + else if(degree <= 15) + { + detail::kernel_2d result(2 * degree + 1, degree, degree); + std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldx); // use enum here + std::copy(dx_sobeln.begin(), dx_sobeln.end(), result.begin()); + return result; + } + else + throw std::length_error("Larger kernels than 31x31 may fail/delay other executions, hence " + "they are not yet provided"); //to not upset compiler throw std::runtime_error("unreachable statement"); } @@ -221,22 +231,30 @@ inline detail::kernel_2d generate_dx_scharr(unsigned int degree = template > inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1) { - switch (degree) + if(degree == 0) + return detail::get_identity_kernel(); + else if (degree == 1) { - case 0: - { - return detail::get_identity_kernel(); - } - case 1: - { - detail::kernel_2d result(3, 1, 1); - std::copy(detail::dy_sobel.begin(), detail::dy_sobel.end(), result.begin()); - return result; - } - default: - throw std::logic_error("not supported yet"); + detail::kernel_2d result(3, 1, 1); + std::copy(detail::dy_sobel.begin(), detail::dy_sobel.end(), result.begin()); + return result; } - + else if(degree == 2) + { + detail::kernel_2d result(5, 2, 2); + std::copy(detail::dy_sobel2.begin(), detail::dy_sobel2.end(), result.begin()); + return result; + } + else if(degree <= 15) + { + detail::kernel_2d result(2 * degree + 1, degree, degree); + std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldy); // use enum here + std::copy(dy_sobeln.begin(), dy_sobeln.end(), result.begin()); + return result; + } + else + throw std::length_error("Larger kernels than 31x31 may fail/delay other executions, hence " + "they are not yet provided"); //to not upset compiler throw std::runtime_error("unreachable statement"); } diff --git a/test/core/image_processing/sobel_scharr.cpp b/test/core/image_processing/sobel_scharr.cpp index 6ebc90cad6..2433c4b6fc 100644 --- a/test/core/image_processing/sobel_scharr.cpp +++ b/test/core/image_processing/sobel_scharr.cpp @@ -1,5 +1,6 @@ // // Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Prathamesh Tagore // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -16,10 +17,212 @@ namespace gil = boost::gil; +// Following vectors are reference kernel vectors obtained from opencv squeezed in to one dimension. +std::vector dx_sobel2 {{ + -1,-2,0,2,1,-4,-8,0,8,4,-6,-12,0,12,6,-4,-8,0,8,4,-1,-2,0,2,1 +}}; + +std::vector dx_sobel3 { + -1, -4, -5, 0, 5, 4, 1, + -6, -24, -30, 0, 30, 24, 6, + -15, -60, -75, 0, 75, 60, 15, + -20, -80, -100, 0, 100, 80, 20, + -15, -60, -75, 0, 75, 60, 15, + -6, -24, -30, 0, 30, 24, 6, + -1, -4, -5, 0, 5, 4, 1 +}; + +std::vector dx_sobel4 { + -1, -6, -14, -14, 0, 14, 14, 6, 1, + -8, -48, -112, -112, 0, 112, 112, 48, 8, + -28, -168, -392, -392, 0, 392, 392, 168, 28, + -56, -336, -784, -784, 0, 784, 784, 336, 56, + -70, -420, -980, -980, 0, 980, 980, 420, 70, + -56, -336, -784, -784, 0, 784, 784, 336, 56, + -28, -168, -392, -392, 0, 392, 392, 168, 28, + -8, -48, -112, -112, 0, 112, 112, 48, 8, + -1, -6, -14, -14, 0, 14, 14, 6, 1 +}; + +std::vector dx_sobel5 { + -1.0000e+00, -8.0000e+00, -2.7000e+01, -4.8000e+01, -4.2000e+01, 0.0000e+00, + 4.2000e+01, 4.8000e+01, 2.7000e+01, 8.0000e+00, 1.0000e+00, + -1.0000e+01, -8.0000e+01, -2.7000e+02, -4.8000e+02, -4.2000e+02, 0.0000e+00, + 4.2000e+02, 4.8000e+02, 2.7000e+02, 8.0000e+01, 1.0000e+01, + -4.5000e+01, -3.6000e+02, -1.2150e+03, -2.1600e+03, -1.8900e+03, 0.0000e+00, + 1.8900e+03, 2.1600e+03, 1.2150e+03, 3.6000e+02, 4.5000e+01, + -1.2000e+02, -9.6000e+02, -3.2400e+03, -5.7600e+03, -5.0400e+03, 0.0000e+00, + 5.0400e+03, 5.7600e+03, 3.2400e+03, 9.6000e+02, 1.2000e+02, + -2.1000e+02, -1.6800e+03, -5.6700e+03, -1.0080e+04, -8.8200e+03, 0.0000e+00, + 8.8200e+03, 1.0080e+04, 5.6700e+03, 1.6800e+03, 2.1000e+02, + -2.5200e+02, -2.0160e+03, -6.8040e+03, -1.2096e+04, -1.0584e+04, 0.0000e+00, + 1.0584e+04, 1.2096e+04, 6.8040e+03, 2.0160e+03, 2.5200e+02, + -2.1000e+02, -1.6800e+03, -5.6700e+03, -1.0080e+04, -8.8200e+03, 0.0000e+00, + 8.8200e+03, 1.0080e+04, 5.6700e+03, 1.6800e+03, 2.1000e+02, + -1.2000e+02, -9.6000e+02, -3.2400e+03, -5.7600e+03, -5.0400e+03, 0.0000e+00, + 5.0400e+03, 5.7600e+03, 3.2400e+03, 9.6000e+02, 1.2000e+02, + -4.5000e+01, -3.6000e+02, -1.2150e+03, -2.1600e+03, -1.8900e+03, 0.0000e+00, + 1.8900e+03, 2.1600e+03, 1.2150e+03, 3.6000e+02, 4.5000e+01, + -1.0000e+01, -8.0000e+01, -2.7000e+02, -4.8000e+02, -4.2000e+02, 0.0000e+00, + 4.2000e+02, 4.8000e+02, 2.7000e+02, 8.0000e+01, 1.0000e+01, + -1.0000e+00, -8.0000e+00, -2.7000e+01, -4.8000e+01, -4.2000e+01, 0.0000e+00, + 4.2000e+01, 4.8000e+01, 2.7000e+01, 8.0000e+00, 1.0000e+00 +}; + +std::vector dx_sobel6 { + -1.00000e+00, -1.00000e+01, -4.40000e+01, -1.10000e+02, -1.65000e+02, + -1.32000e+02, 0.00000e+00, 1.32000e+02, 1.65000e+02, 1.10000e+02, + 4.40000e+01, 1.00000e+01, 1.00000e+00, + -1.20000e+01, -1.20000e+02, -5.28000e+02, -1.32000e+03, -1.98000e+03, + -1.58400e+03, 0.00000e+00, 1.58400e+03, 1.98000e+03, 1.32000e+03, + 5.28000e+02, 1.20000e+02, 1.20000e+01, + -6.60000e+01, -6.60000e+02, -2.90400e+03, -7.26000e+03, -1.08900e+04, + -8.71200e+03, 0.00000e+00, 8.71200e+03, 1.08900e+04, 7.26000e+03, + 2.90400e+03, 6.60000e+02, 6.60000e+01, + -2.20000e+02, -2.20000e+03, -9.68000e+03, -2.42000e+04, -3.63000e+04, + -2.90400e+04, 0.00000e+00, 2.90400e+04, 3.63000e+04, 2.42000e+04, + 9.68000e+03, 2.20000e+03, 2.20000e+02, + -4.95000e+02, -4.95000e+03, -2.17800e+04, -5.44500e+04, -8.16750e+04, + -6.53400e+04, 0.00000e+00, 6.53400e+04, 8.16750e+04, 5.44500e+04, + 2.17800e+04, 4.95000e+03, 4.95000e+02, + -7.92000e+02, -7.92000e+03, -3.48480e+04, -8.71200e+04, -1.30680e+05, + -1.04544e+05, 0.00000e+00, 1.04544e+05, 1.30680e+05, 8.71200e+04, + 3.48480e+04, 7.92000e+03, 7.92000e+02, + -9.24000e+02, -9.24000e+03, -4.06560e+04, -1.01640e+05, -1.52460e+05, + -1.21968e+05, 0.00000e+00, 1.21968e+05, 1.52460e+05, 1.01640e+05, + 4.06560e+04, 9.24000e+03, 9.24000e+02, + -7.92000e+02, -7.92000e+03, -3.48480e+04, -8.71200e+04, -1.30680e+05, + -1.04544e+05, 0.00000e+00, 1.04544e+05, 1.30680e+05, 8.71200e+04, + 3.48480e+04, 7.92000e+03, 7.92000e+02, + -4.95000e+02, -4.95000e+03, -2.17800e+04 ,-5.44500e+04, -8.16750e+04, + -6.53400e+04, 0.00000e+00, 6.53400e+04, 8.16750e+04, 5.44500e+04, + 2.17800e+04, 4.95000e+03, 4.95000e+02, + -2.20000e+02, -2.20000e+03, -9.68000e+03, -2.42000e+04, -3.63000e+04, + -2.90400e+04, 0.00000e+00, 2.90400e+04, 3.63000e+04, 2.42000e+04, + 9.68000e+03, 2.20000e+03, 2.20000e+02, + -6.60000e+01, -6.60000e+02, -2.90400e+03, -7.26000e+03, -1.08900e+04, + -8.71200e+03, 0.00000e+00, 8.71200e+03, 1.08900e+04, 7.26000e+03, + 2.90400e+03, 6.60000e+02, 6.60000e+01, + -1.20000e+01, -1.20000e+02, -5.28000e+02, -1.32000e+03, -1.98000e+03, + -1.58400e+03, 0.00000e+00, 1.58400e+03, 1.98000e+03, 1.32000e+03, + 5.28000e+02, 1.20000e+02, 1.20000e+01, + -1.00000e+00, -1.00000e+01, -4.40000e+01, -1.10000e+02, -1.65000e+02, + -1.32000e+02, 0.00000e+00, 1.32000e+02, 1.65000e+02 , 1.10000e+02, + 4.40000e+01, 1.00000e+01, 1.00000e+00 +}; + +std::vector dy_sobel2 {{ + -1,-4,-6,-4,-1,-2,-8,-12,-8,-2,0,0,0,0,0,2,8,12,8,2,1,4,6,4,1 +}}; + +std::vector dy_sobel3 { + -1, -6, -15, -20, -15, -6, -1, + -4, -24, -60, -80, -60, -24, -4, + -5, -30, -75, -100, -75, -30, -5, + 0, 0, 0, 0, 0, 0, 0, + 5, 30, 75, 100, 75, 30, 5, + 4, 24, 60, 80, 60, 24, 4, + 1, 6, 15, 20, 15, 6, 1 +}; + +std::vector dy_sobel4 { + -1, -8, -28, -56, -70, -56, -28, -8, -1, + -6, -48, -168, -336, -420, -336, -168, -48, -6, + -14, -112, -392, -784, -980, -784, -392, -112, -14, + -14, -112, -392, -784, -980, -784, -392, -112, -14, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 112, 392, 784, 980, 784, 392, 112, 14, + 14, 112, 392, 784, 980, 784, 392, 112, 14, + 6, 48, 168, 336, 420, 336, 168, 48, 6, + 1, 8, 28, 56, 70, 56, 28, 8, 1 +}; + +std::vector dy_sobel5 { + -1.0000e+00, -1.0000e+01, -4.5000e+01, -1.2000e+02, -2.1000e+02, -2.5200e+02, + -2.1000e+02, -1.2000e+02, -4.5000e+01, -1.0000e+01, -1.0000e+00, + -8.0000e+00, -8.0000e+01, -3.6000e+02, -9.6000e+02, -1.6800e+03, -2.0160e+03, + -1.6800e+03, -9.6000e+02, -3.6000e+02, -8.0000e+01, -8.0000e+00, + -2.7000e+01, -2.7000e+02, -1.2150e+03, -3.2400e+03, -5.6700e+03, -6.8040e+03, + -5.6700e+03, -3.2400e+03, -1.2150e+03, -2.7000e+02, -2.7000e+01, + -4.8000e+01, -4.8000e+02, -2.1600e+03, -5.7600e+03, -1.0080e+04, -1.2096e+04, + -1.0080e+04, -5.7600e+03, -2.1600e+03, -4.8000e+02, -4.8000e+01, + -4.2000e+01, -4.2000e+02, -1.8900e+03, -5.0400e+03, -8.8200e+03, -1.0584e+04, + -8.8200e+03, -5.0400e+03, -1.8900e+03, -4.2000e+02, -4.2000e+01, + 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, + 4.2000e+01, 4.2000e+02, 1.8900e+03, 5.0400e+03, 8.8200e+03, 1.0584e+04, + 8.8200e+03, 5.0400e+03, 1.8900e+03, 4.2000e+02, 4.2000e+01, + 4.8000e+01, 4.8000e+02, 2.1600e+03, 5.7600e+03, 1.0080e+04, 1.2096e+04, + 1.0080e+04, 5.7600e+03, 2.1600e+03, 4.8000e+02, 4.8000e+01, + 2.7000e+01, 2.7000e+02, 1.2150e+03, 3.2400e+03, 5.6700e+03, 6.8040e+03, + 5.6700e+03, 3.2400e+03, 1.2150e+03, 2.7000e+02, 2.7000e+01, + 8.0000e+00, 8.0000e+01, 3.6000e+02, 9.6000e+02, 1.6800e+03, 2.0160e+03, + 1.6800e+03, 9.6000e+02, 3.6000e+02, 8.0000e+01, 8.0000e+00, + 1.0000e+00, 1.0000e+01, 4.5000e+01, 1.2000e+02, 2.1000e+02, 2.5200e+02, + 2.1000e+02, 1.2000e+02, 4.5000e+01, 1.0000e+01, 1.0000e+00 +}; + +std::vector dy_sobel6 { + -1.00000e+00, -1.20000e+01, -6.60000e+01, -2.20000e+02, -4.95000e+02, + -7.92000e+02, -9.24000e+02, -7.92000e+02, -4.95000e+02, -2.20000e+02, + -6.60000e+01, -1.20000e+01, -1.00000e+00, + -1.00000e+01, -1.20000e+02, -6.60000e+02, -2.20000e+03, -4.95000e+03, + -7.92000e+03, -9.24000e+03, -7.92000e+03, -4.95000e+03, -2.20000e+03, + -6.60000e+02, -1.20000e+02, -1.00000e+01, + -4.40000e+01, -5.28000e+02, -2.90400e+03, -9.68000e+03, -2.17800e+04, + -3.48480e+04, -4.06560e+04, -3.48480e+04, -2.17800e+04, -9.68000e+03, + -2.90400e+03, -5.28000e+02, -4.40000e+01, + -1.10000e+02, -1.32000e+03, -7.26000e+03, -2.42000e+04, -5.44500e+04, + -8.71200e+04, -1.01640e+05, -8.71200e+04, -5.44500e+04, -2.42000e+04, + -7.26000e+03, -1.32000e+03, -1.10000e+02, + -1.65000e+02, -1.98000e+03, -1.08900e+04, -3.63000e+04, -8.16750e+04, + -1.30680e+05, -1.52460e+05, -1.30680e+05, -8.16750e+04, -3.63000e+04, + -1.08900e+04, -1.98000e+03, -1.65000e+02, + -1.32000e+02, -1.58400e+03, -8.71200e+03, -2.90400e+04, -6.53400e+04, + -1.04544e+05, -1.21968e+05, -1.04544e+05, -6.53400e+04, -2.90400e+04, + -8.71200e+03, -1.58400e+03, -1.32000e+02, + 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, + 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, + 0.00000e+00, 0.00000e+00, 0.00000e+00, + 1.32000e+02, 1.58400e+03, 8.71200e+03, 2.90400e+04, 6.53400e+04, + 1.04544e+05, 1.21968e+05, 1.04544e+05, 6.53400e+04, 2.90400e+04, + 8.71200e+03, 1.58400e+03, 1.32000e+02, + 1.65000e+02, 1.98000e+03, 1.08900e+04, 3.63000e+04, 8.16750e+04, + 1.30680e+05, 1.52460e+05, 1.30680e+05, 8.16750e+04, 3.63000e+04, + 1.08900e+04, 1.98000e+03, 1.65000e+02, + 1.10000e+02, 1.32000e+03, 7.26000e+03, 2.42000e+04, 5.44500e+04, + 8.71200e+04, 1.01640e+05, 8.71200e+04, 5.44500e+04, 2.42000e+04, + 7.26000e+03, 1.32000e+03, 1.10000e+02, + 4.40000e+01, 5.28000e+02, 2.90400e+03, 9.68000e+03, 2.17800e+04, + 3.48480e+04, 4.06560e+04, 3.48480e+04, 2.17800e+04, 9.68000e+03, + 2.90400e+03, 5.28000e+02, 4.40000e+01, + 1.00000e+01, 1.20000e+02, 6.60000e+02, 2.20000e+03, 4.95000e+03, + 7.92000e+03, 9.24000e+03, 7.92000e+03, 4.95000e+03, 2.20000e+03, + 6.60000e+02, 1.20000e+02, 1.00000e+01, + 1.00000e+00, 1.20000e+01, 6.60000e+01, 2.20000e+02, 4.95000e+02, + 7.92000e+02, 9.24000e+02, 7.92000e+02, 4.95000e+02, 2.20000e+02, + 6.60000e+01, 1.20000e+01, 1.00000e+00 +}; + void test_dx_sobel_kernel() { auto const kernel = gil::generate_dx_sobel(1); BOOST_TEST_ALL_EQ(kernel.begin(), kernel.end(), gil::detail::dx_sobel.begin(), gil::detail::dx_sobel.end()); + + auto const kernel2 = gil::generate_dx_sobel(2); + BOOST_TEST_ALL_EQ(kernel2.begin(), kernel2.end(), dx_sobel2.begin(), dx_sobel2.end()); + + auto const kernel3 = gil::generate_dx_sobel(3); + BOOST_TEST_ALL_EQ(kernel3.begin(), kernel3.end(), dx_sobel3.begin(), dx_sobel3.end()); + + auto const kernel4 = gil::generate_dx_sobel(4); + BOOST_TEST_ALL_EQ(kernel4.begin(), kernel4.end(), dx_sobel4.begin(), dx_sobel4.end()); + + auto const kernel5 = gil::generate_dx_sobel(5); + BOOST_TEST_ALL_EQ(kernel5.begin(), kernel5.end(), dx_sobel5.begin(), dx_sobel5.end()); + + auto const kernel6 = gil::generate_dx_sobel(6); + BOOST_TEST_ALL_EQ(kernel6.begin(), kernel6.end(), dx_sobel6.begin(), dx_sobel6.end()); } void test_dx_scharr_kernel() @@ -32,6 +235,21 @@ void test_dy_sobel_kernel() { auto const kernel = gil::generate_dy_sobel(1); BOOST_TEST_ALL_EQ(kernel.begin(), kernel.end(), gil::detail::dy_sobel.begin(), gil::detail::dy_sobel.end()); + + auto const kernel2 = gil::generate_dy_sobel(2); + BOOST_TEST_ALL_EQ(kernel2.begin(), kernel2.end(), dy_sobel2.begin(), dy_sobel2.end()); + + auto const kernel3 = gil::generate_dy_sobel(3); + BOOST_TEST_ALL_EQ(kernel3.begin(), kernel3.end(), dy_sobel3.begin(), dy_sobel3.end()); + + auto const kernel4 = gil::generate_dy_sobel(4); + BOOST_TEST_ALL_EQ(kernel4.begin(), kernel4.end(), dy_sobel4.begin(), dy_sobel4.end()); + + auto const kernel5 = gil::generate_dy_sobel(5); + BOOST_TEST_ALL_EQ(kernel5.begin(), kernel5.end(), dy_sobel5.begin(), dy_sobel5.end()); + + auto const kernel6 = gil::generate_dy_sobel(6); + BOOST_TEST_ALL_EQ(kernel6.begin(), kernel6.end(), dy_sobel6.begin(), dy_sobel6.end()); } void test_dy_scharr_kernel() From ce1c25e736c1ac9a99997175d8763fb90be7825b Mon Sep 17 00:00:00 2001 From: meshtag Date: Fri, 19 Feb 2021 20:56:11 +0530 Subject: [PATCH 08/23] Improved comments --- test/core/image_processing/sobel_scharr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/core/image_processing/sobel_scharr.cpp b/test/core/image_processing/sobel_scharr.cpp index 2433c4b6fc..23a82d5844 100644 --- a/test/core/image_processing/sobel_scharr.cpp +++ b/test/core/image_processing/sobel_scharr.cpp @@ -18,6 +18,9 @@ namespace gil = boost::gil; // Following vectors are reference kernel vectors obtained from opencv squeezed in to one dimension. +// Number written after "dx_sobel" represents the order of derivative used. +// Ex. : "2" in "dx_sobel2" signifies that "dx_sobel2" contains refernce kernel for second order +// sobel derivative,"dx" signifies that the derivative is for horizontal direction. std::vector dx_sobel2 {{ -1,-2,0,2,1,-4,-8,0,8,4,-6,-12,0,12,6,-4,-8,0,8,4,-1,-2,0,2,1 }}; From 1a76034220456fa9b7f80b54de183a1772784f73 Mon Sep 17 00:00:00 2001 From: meshtag Date: Fri, 19 Feb 2021 22:14:47 +0530 Subject: [PATCH 09/23] Improved documentation --- include/boost/gil/detail/math.hpp | 29 ++++++++++++------- .../boost/gil/image_processing/numeric.hpp | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index ccf715c90b..d7822ff584 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -46,15 +46,24 @@ inline detail::kernel_2d get_identity_kernel() // Please refer https://stackoverflow.com/a/10032882/14958679 for getting an overview of the // concept applied for obtaining higher order sobel kernels. -/// \brief Produces higher order kernel vector by performing discrete convolution between lower +/// \defgroup KernelGeneration +/// \brief Contains documentation for functions used in kernel generation. +/// +/// Separate functions are used for generating only those kernels whose dimensions are greater than +/// 5x5. Smaller kernels are fed directly to the algorithm. +/// + +/// \addtogroup KernelGeneration +/// @{ + +/// \brief Produces higher order kernel vector by performing discrete convolution between lower /// order kernel vector and a smoothing kernel vector(kernel used for suppressing noise). -/// \ingroup ImageProcessingMath /// \param kernel1 - First argument for kernel vector convolution. /// \param kernel2 - Second argument for kernel vector convolution. -/// \tparam T1 Type of first argument for kernel vector convolution. -/// \tparam T2 Type of second argument for kernel vector convolution. +/// \tparam T1 - Type of first argument for kernel vector convolution. +/// \tparam T2 - Type of second argument for kernel vector convolution. template -static std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) +std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) { size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; std::vector> convolved_kernel(convolved_kernel_size, @@ -109,11 +118,10 @@ static std::vector> kernel_convolve_impl(T1 kernel1, T2 kerne return convolved_kernel; } -/// \brief Fills kernel vector given as argument with a second order kernel in horizontal or +/// \brief Fills kernel vector given as argument with a second order kernel in horizontal or /// vertical direction. The type of the kernel which is to be used for filling will be indicated /// by the variable 'type'. -/// \ingroup ImageProcessingMath -/// \param kernel - kernel vector which will be filled. +/// \param kernel - Kernel vector which will be filled. /// \param type - Indicates the type of second order derivative kernel which is to be filled inside /// first argument. void kernel_fill(std::vector>& kernel, kernel_type type) @@ -134,8 +142,7 @@ void kernel_fill(std::vector>& kernel, kernel_type type) /// \brief Passes parameters to 'kernel_convolve_impl()' repeatedly until kernel vector of desired /// order is obtained. -/// \ingroup ImageProcessingMath -/// \param order - Indicates the order of kernel vector which is to be returned. +/// \param order - Indicates order of derivative whose kernel vector is to be returned. /// \param type - Indicates the type of kernel vector which is to be returned. std::vector kernel_convolve(unsigned int order, kernel_type type) { @@ -153,7 +160,7 @@ std::vector kernel_convolve(unsigned int order, kernel_type type) return convolved_kernel_flatten; } - +/// @} }}} // namespace boost::gil::detail #endif diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index ba1b89cdee..2857ac33c2 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -50,7 +50,7 @@ inline double lanczos(double x, std::ptrdiff_t a) if (0 <= x && x <= 0) return 1; - if (-a < x && x < a) + if (static_cast(-a) < x && x < static_cast(a)) return normalized_sinc(x) / normalized_sinc(x / static_cast(a)); return 0; From 37d65f4a01ea298e1222383f663b8d6cf98abf43 Mon Sep 17 00:00:00 2001 From: meshtag Date: Fri, 19 Feb 2021 22:19:57 +0530 Subject: [PATCH 10/23] Correct typo --- include/boost/gil/image_processing/numeric.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 2857ac33c2..7bd4da7f8d 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -180,7 +180,7 @@ inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1 else if(degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); - std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldx); // use enum here + std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldx); std::copy(dx_sobeln.begin(), dx_sobeln.end(), result.begin()); return result; } @@ -248,7 +248,7 @@ inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1 else if(degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); - std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldy); // use enum here + std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldy); std::copy(dy_sobeln.begin(), dy_sobeln.end(), result.begin()); return result; } From 0b1a1d491a09db3a4c8d97646027e03e1d064c9d Mon Sep 17 00:00:00 2001 From: meshtag Date: Sat, 27 Feb 2021 17:57:57 +0530 Subject: [PATCH 11/23] Initial_Corrections --- include/boost/gil/detail/math.hpp | 25 +++++++++++-------- .../boost/gil/image_processing/numeric.hpp | 8 ++---- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index d7822ff584..afb4947d58 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -9,16 +9,16 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_DETAIL_MATH_HPP #define BOOST_GIL_IMAGE_PROCESSING_DETAIL_MATH_HPP -#include #include +#include #include namespace boost { namespace gil { namespace detail { enum class kernel_type { - sobeldx, - sobeldy, + sobel_dx, + sobel_dy, }; static constexpr double pi = 3.14159265358979323846; @@ -27,11 +27,13 @@ static constexpr std::array dx_sobel = {{-1, 0, 1, -2, 0, 2, -1, 0, 1} static constexpr std::array dx_sobel2 = {{ -1,-2,0,2,1,-4,-8,0,8,4,-6,-12,0,12,6,-4,-8,0,8,4,-1,-2,0,2,1 }}; +// In dx_sobel2, "2" indicates that the order of Sobel derivative in x-direction is 2. static constexpr std::array dx_scharr = {{-1, 0, 1, -1, 0, 1, -1, 0, 1}}; static constexpr std::array dy_sobel = {{1, 2, 1, 0, 0, 0, -1, -2, -1}}; static constexpr std::array dy_sobel2 = {{ -1,-4,-6,-4,-1,-2,-8,-12,-8,-2,0,0,0,0,0,2,8,12,8,2,1,4,6,4,1 }}; +// In dy_sobel2, "2" indicates that the order of Sobel derivative in y-direction is 2. static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; static std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; @@ -44,7 +46,7 @@ inline detail::kernel_2d get_identity_kernel() } // Please refer https://stackoverflow.com/a/10032882/14958679 for getting an overview of the -// concept applied for obtaining higher order sobel kernels. +// concept applied for obtaining higher order Sobel kernels. /// \defgroup KernelGeneration /// \brief Contains documentation for functions used in kernel generation. @@ -63,13 +65,12 @@ inline detail::kernel_2d get_identity_kernel() /// \tparam T1 - Type of first argument for kernel vector convolution. /// \tparam T2 - Type of second argument for kernel vector convolution. template -std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) +auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> { size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; std::vector> convolved_kernel(convolved_kernel_size, - std::vector(convolved_kernel_size)), - dummy_kernel(convolved_kernel_size, - std::vector(convolved_kernel_size)); + std::vector(convolved_kernel_size)),dummy_kernel(convolved_kernel_size, + std::vector(convolved_kernel_size)); // 'dummy_kernel' will be made by padding 'kernel1' with appropriate no. of rows and columns // containing zeros to match the size specified by 'convolved_kernel_size'. @@ -80,11 +81,13 @@ std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) std::ptrdiff_t padding_reference = (kernel2.size() - 1) / 2; for(std::ptrdiff_t row = padding_reference;row < dummy_kernel.size() - padding_reference; ++row) + { for(std::ptrdiff_t col = padding_reference;col < dummy_kernel.size() - padding_reference; ++col) { dummy_kernel[row][col] = kernel1[row - padding_reference][col - padding_reference]; } + } std::ptrdiff_t flip_ker_row, flip_ker_col, row_boundary, col_boundary; float aux_total; @@ -126,13 +129,13 @@ std::vector> kernel_convolve_impl(T1 kernel1, T2 kernel2) /// first argument. void kernel_fill(std::vector>& kernel, kernel_type type) { - if(type == kernel_type::sobeldx) + if(type == kernel_type::sobel_dx) { for(std::ptrdiff_t row = 0;row < 5; ++row) for(std::ptrdiff_t col = 0;col < 5; ++col) kernel[row][col] = dx_sobel2[5 * row + col]; } - else if(type == kernel_type::sobeldy) + else if(type == kernel_type::sobel_dy) { for(std::ptrdiff_t row =0;row < 5; ++row) for(std::ptrdiff_t col = 0;col < 5; ++col) @@ -144,7 +147,7 @@ void kernel_fill(std::vector>& kernel, kernel_type type) /// order is obtained. /// \param order - Indicates order of derivative whose kernel vector is to be returned. /// \param type - Indicates the type of kernel vector which is to be returned. -std::vector kernel_convolve(unsigned int order, kernel_type type) +auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector { std::vector convolved_kernel_flatten; std::vector> convolved_kernel(5, std::vector(5)); diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 7bd4da7f8d..0f10d5728d 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -180,15 +180,13 @@ inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1 else if(degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); - std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldx); + std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobel_dx); std::copy(dx_sobeln.begin(), dx_sobeln.end(), result.begin()); return result; } else throw std::length_error("Larger kernels than 31x31 may fail/delay other executions, hence " "they are not yet provided"); - //to not upset compiler - throw std::runtime_error("unreachable statement"); } /// \brief Generate Scharr operator in horizontal direction @@ -248,15 +246,13 @@ inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1 else if(degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); - std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobeldy); + std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobel_dy); std::copy(dy_sobeln.begin(), dy_sobeln.end(), result.begin()); return result; } else throw std::length_error("Larger kernels than 31x31 may fail/delay other executions, hence " "they are not yet provided"); - //to not upset compiler - throw std::runtime_error("unreachable statement"); } /// \brief Generate Scharr operator in vertical direction From 94829734b8206cc6f9a5121b3e36a20c88ed8886 Mon Sep 17 00:00:00 2001 From: meshtag Date: Sun, 28 Feb 2021 17:04:54 +0530 Subject: [PATCH 12/23] modify variable name --- include/boost/gil/detail/math.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index afb4947d58..b81aebba44 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -76,16 +76,16 @@ auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector Date: Sun, 28 Feb 2021 17:32:48 +0530 Subject: [PATCH 13/23] Should handle builds --- include/boost/gil/detail/math.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index b81aebba44..02ea306355 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -127,7 +127,7 @@ auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector>& kernel, kernel_type type) +void kernel_vector_fill(std::vector>& kernel, kernel_type type) { if(type == kernel_type::sobel_dx) { @@ -152,7 +152,7 @@ auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector std::vector convolved_kernel_flatten; std::vector> convolved_kernel(5, std::vector(5)); - kernel_fill(convolved_kernel, type); + kernel_vector_fill(convolved_kernel, type); for(unsigned int i = 0;i < order - 2; ++i) convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); From 9335e17ba9a7c8367005e735008d23cc01845cef Mon Sep 17 00:00:00 2001 From: meshtag Date: Sun, 28 Feb 2021 20:09:37 +0530 Subject: [PATCH 14/23] Improved comments --- include/boost/gil/detail/math.hpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 02ea306355..422536cd63 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -27,13 +27,15 @@ static constexpr std::array dx_sobel = {{-1, 0, 1, -2, 0, 2, -1, 0, 1} static constexpr std::array dx_sobel2 = {{ -1,-2,0,2,1,-4,-8,0,8,4,-6,-12,0,12,6,-4,-8,0,8,4,-1,-2,0,2,1 }}; -// In dx_sobel2, "2" indicates that the order of Sobel derivative in x-direction is 2. +// In variable name "dx_sobel2", "2" indicates that the order of Sobel derivative in x-direction +// is 2. static constexpr std::array dx_scharr = {{-1, 0, 1, -1, 0, 1, -1, 0, 1}}; static constexpr std::array dy_sobel = {{1, 2, 1, 0, 0, 0, -1, -2, -1}}; static constexpr std::array dy_sobel2 = {{ -1,-4,-6,-4,-1,-2,-8,-12,-8,-2,0,0,0,0,0,2,8,12,8,2,1,4,6,4,1 }}; -// In dy_sobel2, "2" indicates that the order of Sobel derivative in y-direction is 2. +// In variable name "dy_sobel2", "2" indicates that the order of Sobel derivative in y-direction +// is 2. static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; static std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; @@ -74,9 +76,11 @@ auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector std::vector= 0 && row_boundary < convolved_kernel_size && col_boundary >= 0 && col_boundary < convolved_kernel_size) { aux_total += - kernel2[flip_ker_row][flip_ker_col] * + kernel2[flip_kernel_row][flip_kernel_col] * dummy_kernel[row_boundary][col_boundary]; } } From 1c60bafc203e061fde3d3085666ede9c705f1da2 Mon Sep 17 00:00:00 2001 From: meshtag Date: Mon, 1 Mar 2021 00:02:21 +0530 Subject: [PATCH 15/23] Should handle builds --- include/boost/gil/detail/math.hpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 422536cd63..7c9284a1ae 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -37,7 +37,7 @@ static constexpr std::array dy_sobel2 = {{ // In variable name "dy_sobel2", "2" indicates that the order of Sobel derivative in y-direction // is 2. static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; -static std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; +static const std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; template inline detail::kernel_2d get_identity_kernel() @@ -67,7 +67,7 @@ inline detail::kernel_2d get_identity_kernel() /// \tparam T1 - Type of first argument for kernel vector convolution. /// \tparam T2 - Type of second argument for kernel vector convolution. template -auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> +static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> { size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; std::vector> convolved_kernel(convolved_kernel_size, @@ -131,19 +131,27 @@ auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector>& kernel, kernel_type type) +static void kernel_vector_fill(std::vector>& kernel, kernel_type type) { if(type == kernel_type::sobel_dx) { for(std::ptrdiff_t row = 0;row < 5; ++row) + { for(std::ptrdiff_t col = 0;col < 5; ++col) + { kernel[row][col] = dx_sobel2[5 * row + col]; + } + } } else if(type == kernel_type::sobel_dy) { for(std::ptrdiff_t row =0;row < 5; ++row) + { for(std::ptrdiff_t col = 0;col < 5; ++col) + { kernel[row][col] = dy_sobel2[5 * row + col]; + } + } } } @@ -151,7 +159,7 @@ void kernel_vector_fill(std::vector>& kernel, kernel_type typ /// order is obtained. /// \param order - Indicates order of derivative whose kernel vector is to be returned. /// \param type - Indicates the type of kernel vector which is to be returned. -auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector +static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector { std::vector convolved_kernel_flatten; std::vector> convolved_kernel(5, std::vector(5)); @@ -159,11 +167,17 @@ auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector kernel_vector_fill(convolved_kernel, type); for(unsigned int i = 0;i < order - 2; ++i) + { convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); + } for(std::ptrdiff_t row = 0;row < convolved_kernel.size(); ++row) + { for(std::ptrdiff_t col = 0;col < convolved_kernel.size(); ++col) + { convolved_kernel_flatten.push_back(convolved_kernel[row][col]); + } + } return convolved_kernel_flatten; } From 3c19e11978fd1a64b9b8247ab249ab02949b18d9 Mon Sep 17 00:00:00 2001 From: meshtag Date: Wed, 3 Mar 2021 11:03:21 +0530 Subject: [PATCH 16/23] Improved time complexity --- include/boost/gil/detail/math.hpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 7c9284a1ae..5a64f62ab5 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace boost { namespace gil { namespace detail { @@ -37,7 +38,7 @@ static constexpr std::array dy_sobel2 = {{ // In variable name "dy_sobel2", "2" indicates that the order of Sobel derivative in y-direction // is 2. static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; -static const std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; +std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; template inline detail::kernel_2d get_identity_kernel() @@ -165,8 +166,27 @@ static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector std::vector> convolved_kernel(5, std::vector(5)); kernel_vector_fill(convolved_kernel, type); - - for(unsigned int i = 0;i < order - 2; ++i) + + // Variable 'smoothing_dummy' will be used for storing and calculating results of repeated + // convolution of 'smoothing_kernel' with itself. + std::vector>smoothing_dummy = smoothing_kernel; + + // Variable 'smooth_repetition' will store the number of times we need to convolve + // 'smoothing_dummy' with itself. This number when used as power of 2 in its exponentiation, + // will result in a number which is the largest power of 2 smaller than 'order - 2'. + unsigned int smooth_repetition = (unsigned int)log2(order - 2); + + for(unsigned int i = 0;i < smooth_repetition; ++i) + { + smoothing_dummy = kernel_convolve_impl(smoothing_dummy, smoothing_dummy); + } + + convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_dummy); + + // Variable 'order_decrease' will store the amount of decrease in order obtained due to the above + // optimization. It stores the largest power of 2 smaller than 'order - 2'. + unsigned int order_decrease = pow(2, smooth_repetition); + for(unsigned int i = 0;i < order - 2 - order_decrease; ++i) { convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); } From 7eb7088399592d9447e3fd4bee461f6d42b0b762 Mon Sep 17 00:00:00 2001 From: meshtag Date: Wed, 3 Mar 2021 11:19:01 +0530 Subject: [PATCH 17/23] Should handle builds --- include/boost/gil/detail/math.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 5a64f62ab5..89f065da00 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -38,7 +38,7 @@ static constexpr std::array dy_sobel2 = {{ // In variable name "dy_sobel2", "2" indicates that the order of Sobel derivative in y-direction // is 2. static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; -std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; +static const std::vector> smoothing_kernel {{1,2,1},{2,4,2},{1,2,1}}; template inline detail::kernel_2d get_identity_kernel() From 5f40cd6d33d996251ec1daba284f0e08d8ea0c9c Mon Sep 17 00:00:00 2001 From: meshtag Date: Wed, 3 Mar 2021 12:31:31 +0530 Subject: [PATCH 18/23] Improved comments --- include/boost/gil/detail/math.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 89f065da00..09b639c235 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -167,12 +167,10 @@ static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector kernel_vector_fill(convolved_kernel, type); - // Variable 'smoothing_dummy' will be used for storing and calculating results of repeated - // convolution of 'smoothing_kernel' with itself. std::vector>smoothing_dummy = smoothing_kernel; // Variable 'smooth_repetition' will store the number of times we need to convolve - // 'smoothing_dummy' with itself. This number when used as power of 2 in its exponentiation, + // 'smoothing_dummy' with itself. This number when used as a power of 2 in its exponentiation, // will result in a number which is the largest power of 2 smaller than 'order - 2'. unsigned int smooth_repetition = (unsigned int)log2(order - 2); From 7c8f33285306a421ed7c3755fc354d3bc9307ff2 Mon Sep 17 00:00:00 2001 From: meshtag Date: Thu, 4 Mar 2021 09:29:42 +0530 Subject: [PATCH 19/23] Improved casting --- include/boost/gil/detail/math.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 09b639c235..79dd0bcc11 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -72,7 +72,8 @@ static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> convolved_kernel(convolved_kernel_size, - std::vector(convolved_kernel_size)),dummy_kernel(convolved_kernel_size, + std::vector(convolved_kernel_size)); + std::vector> dummy_kernel(convolved_kernel_size, std::vector(convolved_kernel_size)); // 'dummy_kernel' will be made by padding 'kernel1' with appropriate no. of rows and columns @@ -95,7 +96,7 @@ static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector std::vector>& kernel, kernel_type type) +void kernel_vector_fill(std::vector>& kernel, kernel_type type) { if(type == kernel_type::sobel_dx) { @@ -167,12 +168,12 @@ static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector kernel_vector_fill(convolved_kernel, type); - std::vector>smoothing_dummy = smoothing_kernel; + std::vector> smoothing_dummy = smoothing_kernel; // Variable 'smooth_repetition' will store the number of times we need to convolve // 'smoothing_dummy' with itself. This number when used as a power of 2 in its exponentiation, // will result in a number which is the largest power of 2 smaller than 'order - 2'. - unsigned int smooth_repetition = (unsigned int)log2(order - 2); + const unsigned int smooth_repetition = static_cast(std::log2(order - 2)); for(unsigned int i = 0;i < smooth_repetition; ++i) { @@ -183,7 +184,7 @@ static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector // Variable 'order_decrease' will store the amount of decrease in order obtained due to the above // optimization. It stores the largest power of 2 smaller than 'order - 2'. - unsigned int order_decrease = pow(2, smooth_repetition); + const unsigned int order_decrease = std::pow(2, smooth_repetition); for(unsigned int i = 0;i < order - 2 - order_decrease; ++i) { convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); From 790f9bce5bedbb85c3420e6a4df4810dba2ab611 Mon Sep 17 00:00:00 2001 From: meshtag Date: Thu, 4 Mar 2021 12:13:42 +0530 Subject: [PATCH 20/23] pass build --- include/boost/gil/detail/math.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 79dd0bcc11..a79fde9ba8 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -133,7 +133,7 @@ static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector>& kernel, kernel_type type) +static void kernel_vector_fill(std::vector>& kernel, kernel_type type) { if(type == kernel_type::sobel_dx) { From f027c8a543b4b313417347158b535aa53e5864a5 Mon Sep 17 00:00:00 2001 From: meshtag Date: Wed, 31 Mar 2021 16:38:22 +0530 Subject: [PATCH 21/23] inline replaces static --- include/boost/gil/detail/math.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index a79fde9ba8..5402e6de6c 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -68,7 +68,7 @@ inline detail::kernel_2d get_identity_kernel() /// \tparam T1 - Type of first argument for kernel vector convolution. /// \tparam T2 - Type of second argument for kernel vector convolution. template -static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> +inline auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> { size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; std::vector> convolved_kernel(convolved_kernel_size, @@ -133,7 +133,7 @@ static auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector>& kernel, kernel_type type) +inline void kernel_vector_fill(std::vector>& kernel, kernel_type type) { if(type == kernel_type::sobel_dx) { @@ -161,7 +161,7 @@ static void kernel_vector_fill(std::vector>& kernel, kernel_t /// order is obtained. /// \param order - Indicates order of derivative whose kernel vector is to be returned. /// \param type - Indicates the type of kernel vector which is to be returned. -static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector +inline auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector { std::vector convolved_kernel_flatten; std::vector> convolved_kernel(5, std::vector(5)); @@ -202,5 +202,5 @@ static auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector } /// @} }}} // namespace boost::gil::detail - + #endif From bb0d9ddb50883b4fbf3158fe65d0a876e25fa727 Mon Sep 17 00:00:00 2001 From: meshtag Date: Thu, 1 Apr 2021 15:53:19 +0530 Subject: [PATCH 22/23] Improve formatting and suppress compiler warnings --- include/boost/gil/detail/math.hpp | 40 ++++++++++--------- .../boost/gil/image_processing/numeric.hpp | 12 +++--- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 5402e6de6c..d9b1948cce 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -70,7 +70,7 @@ inline detail::kernel_2d get_identity_kernel() template inline auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector> { - size_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; + std::ptrdiff_t convolved_kernel_size = kernel1.size() + kernel2.size() - 1; std::vector> convolved_kernel(convolved_kernel_size, std::vector(convolved_kernel_size)); std::vector> dummy_kernel(convolved_kernel_size, @@ -87,9 +87,10 @@ inline auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector std::vector std::vector>& kernel, kernel_type type) { - if(type == kernel_type::sobel_dx) + if (type == kernel_type::sobel_dx) { - for(std::ptrdiff_t row = 0;row < 5; ++row) + for (std::ptrdiff_t row = 0; row < 5; ++row) { - for(std::ptrdiff_t col = 0;col < 5; ++col) + for (std::ptrdiff_t col = 0; col < 5; ++col) { kernel[row][col] = dx_sobel2[5 * row + col]; } } } - else if(type == kernel_type::sobel_dy) + else if (type == kernel_type::sobel_dy) { - for(std::ptrdiff_t row =0;row < 5; ++row) + for (std::ptrdiff_t row =0; row < 5; ++row) { - for(std::ptrdiff_t col = 0;col < 5; ++col) + for (std::ptrdiff_t col = 0; col < 5; ++col) { kernel[row][col] = dy_sobel2[5 * row + col]; } @@ -173,9 +175,9 @@ inline auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector // Variable 'smooth_repetition' will store the number of times we need to convolve // 'smoothing_dummy' with itself. This number when used as a power of 2 in its exponentiation, // will result in a number which is the largest power of 2 smaller than 'order - 2'. - const unsigned int smooth_repetition = static_cast(std::log2(order - 2)); + const double smooth_repetition = static_cast(std::log2(order - 2)); - for(unsigned int i = 0;i < smooth_repetition; ++i) + for (unsigned int i = 0; i < smooth_repetition; ++i) { smoothing_dummy = kernel_convolve_impl(smoothing_dummy, smoothing_dummy); } @@ -184,15 +186,15 @@ inline auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector // Variable 'order_decrease' will store the amount of decrease in order obtained due to the above // optimization. It stores the largest power of 2 smaller than 'order - 2'. - const unsigned int order_decrease = std::pow(2, smooth_repetition); - for(unsigned int i = 0;i < order - 2 - order_decrease; ++i) + const double order_decrease = std::pow(2, smooth_repetition); + for (unsigned int i = 0; i < order - 2 - order_decrease; ++i) { convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); } - for(std::ptrdiff_t row = 0;row < convolved_kernel.size(); ++row) + for (long unsigned int row = 0; row < convolved_kernel.size(); ++row) { - for(std::ptrdiff_t col = 0;col < convolved_kernel.size(); ++col) + for (long unsigned int col = 0; col < convolved_kernel.size(); ++col) { convolved_kernel_flatten.push_back(convolved_kernel[row][col]); } diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 0f10d5728d..a5384251ff 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -163,7 +163,7 @@ inline detail::kernel_2d generate_gaussian_kernel(std::size_t side template > inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1) { - if(degree == 0) + if (degree == 0) return detail::get_identity_kernel(); else if (degree == 1) { @@ -171,13 +171,13 @@ inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1 std::copy(detail::dx_sobel.begin(), detail::dx_sobel.end(), result.begin()); return result; } - else if(degree == 2) + else if (degree == 2) { detail::kernel_2d result(5, 2, 2); std::copy(detail::dx_sobel2.begin(), detail::dx_sobel2.end(), result.begin()); return result; } - else if(degree <= 15) + else if (degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); std::vector dx_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobel_dx); @@ -229,7 +229,7 @@ inline detail::kernel_2d generate_dx_scharr(unsigned int degree = template > inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1) { - if(degree == 0) + if (degree == 0) return detail::get_identity_kernel(); else if (degree == 1) { @@ -237,13 +237,13 @@ inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1 std::copy(detail::dy_sobel.begin(), detail::dy_sobel.end(), result.begin()); return result; } - else if(degree == 2) + else if (degree == 2) { detail::kernel_2d result(5, 2, 2); std::copy(detail::dy_sobel2.begin(), detail::dy_sobel2.end(), result.begin()); return result; } - else if(degree <= 15) + else if (degree <= 15) { detail::kernel_2d result(2 * degree + 1, degree, degree); std::vector dy_sobeln = detail::kernel_convolve(degree, detail::kernel_type::sobel_dy); From 155de1cc00679fb2a9f793667626b6a023482dde Mon Sep 17 00:00:00 2001 From: meshtag Date: Thu, 1 Apr 2021 18:36:45 +0530 Subject: [PATCH 23/23] Suppress compiler warnings --- include/boost/gil/detail/math.hpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index d9b1948cce..83212e240a 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -87,10 +87,11 @@ inline auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector(dummy_kernel.size()) - padding_origin; ++row) { - for (long unsigned int col = padding_origin; col < dummy_kernel.size() - padding_origin; - ++col) + for (std::ptrdiff_t col = padding_origin; + col < static_cast(dummy_kernel.size()) - padding_origin; ++col) { dummy_kernel[row][col] = kernel1[row - padding_origin][col - padding_origin]; } @@ -103,11 +104,12 @@ inline auto kernel_convolve_impl(T1 kernel1, T2 kernel2) -> std::vector(kernel2.size()); ++kernel2_row) { flip_kernel_row = kernel2.size() - 1 - kernel2_row; - for (long unsigned int kernel2_col = 0; kernel2_col < kernel2.size(); - ++kernel2_col) + for (std::ptrdiff_t kernel2_col = 0; + kernel2_col < static_cast(kernel2.size()); ++kernel2_col) { flip_kernel_col = kernel2.size() - 1 - kernel2_col; row_boundary = dummy_row + kernel2.size()/2 - flip_kernel_row; @@ -175,7 +177,7 @@ inline auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector // Variable 'smooth_repetition' will store the number of times we need to convolve // 'smoothing_dummy' with itself. This number when used as a power of 2 in its exponentiation, // will result in a number which is the largest power of 2 smaller than 'order - 2'. - const double smooth_repetition = static_cast(std::log2(order - 2)); + double const smooth_repetition = static_cast(std::log2(order - 2)); for (unsigned int i = 0; i < smooth_repetition; ++i) { @@ -186,20 +188,21 @@ inline auto kernel_convolve(unsigned int order, kernel_type type) -> std::vector // Variable 'order_decrease' will store the amount of decrease in order obtained due to the above // optimization. It stores the largest power of 2 smaller than 'order - 2'. - const double order_decrease = std::pow(2, smooth_repetition); + double const order_decrease = std::pow(2, smooth_repetition); for (unsigned int i = 0; i < order - 2 - order_decrease; ++i) { convolved_kernel = kernel_convolve_impl(convolved_kernel, smoothing_kernel); } - for (long unsigned int row = 0; row < convolved_kernel.size(); ++row) + for (std::ptrdiff_t row = 0; row < static_cast(convolved_kernel.size()); ++row) { - for (long unsigned int col = 0; col < convolved_kernel.size(); ++col) + for (std::ptrdiff_t col = 0; col < static_cast(convolved_kernel.size()); + ++col) { convolved_kernel_flatten.push_back(convolved_kernel[row][col]); } } - + return convolved_kernel_flatten; } /// @}