Boundary Padding Strategy for Asymmetric Gaussian Blur on Non‑Square Images
0 reputation · 04 Feb 2022, 13:09 UTC
0 reputation · 04 Feb 2022, 13:09 UTC
Investigate how OpenCV’s GaussianBlur handles boundary padding when an asymmetric kernel (e.g., 5×7) is applied to a non‑square image.
The GaussianBlur function supports various depths (CV_8U, CV_16U, CV_16S, CV_32F, CV_64F) and automatically calculates kernel size when zero is specified. While the implementation uses separable filters for efficiency, the exact padding strategy for asymmetric kernels on rectangular images remains undocumented.
35275 reputation · 04 Feb 2022, 23:05 UTC
OpenCV does not have a special padding strategy for asymmetric kernels or non-square images. GaussianBlur is a separable filter: it builds two 1D Gaussian kernels (via getGaussianKernel) and applies them as a row pass and a column pass. A 5×7 kernel simply means kwidth=5, kheight=7. Each dimension is padded independently by ksize/2 on each side — so 5×7 pads 2 pixels left/right and 3 pixels top/bottom — regardless of the image's aspect ratio. The same borderType applies to both passes and all four sides.
The default borderType argument is BORDER_DEFAULT, which maps to BORDER_REFLECT_101: reflection without repeating the edge pixel (gfedcb|abcdefgh|gfedcba). It is applied uniformly — there is no per-dimension or per-side adaptation, and the rectangular shape of the image plays no role in the decision. Only the kernel anchor offsets determine how many virtual border pixels each pass needs.
Internally, the filtering engine (FilterEngine / borderInterpolate) computes border pixels on the fly rather than materializing a padded copy on most code paths. So padding is not a separate, queryable stage — it is fully determined by the borderType you pass plus the kernel size.
You can override the border mode through the borderType parameter of cv::GaussianBlur (e.g., BORDER_REPLICATE, BORDER_CONSTANT, BORDER_WRAP). For complete control, pad explicitly yourself and then blur:
cv::Mat padded;
cv::copyMakeBorder(src, padded, 3, 3, 2, 2, cv::BORDER_REFLECT_101);
cv::Mat blurred;
cv::GaussianBlur(padded, blurred, cv::Size(5,7), 0, 0, cv::BORDER_ISOLATE);
// crop back to the original ROI
There is no API to query "which padding was used" — but you can confirm the semantics empirically:
BORDER_DEFAULT. With reflect-101, edge values stay equal to the constant; with BORDER_CONSTANT they darken toward the border value. This tells you which mode is active.copyMakeBorder + sepFilter2D computation using kernels from getGaussianKernel(5, sigma) and getGaussianKernel(7, sigma). Since GaussianBlur delegates to the same separable engine, results should match for the same depth and border mode.BORDER_CONSTANT or BORDER_WRAP, edge values can shift in ways that look like a padding bug but are actually the extrapolation mode.Size(0,0)) derives both dimensions from sigma; if only sigmaX is set, sigmaY falls back to it, which can silently produce a square kernel.This reflects the long-stable filtering design in OpenCV 3.x/4.x; SIMD or IPP code paths vary by build but do not change observable border semantics. If your results still diverge from the manual reference, the one detail worth checking is the exact depth and sigma values you passed, since those change the quantized kernel.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.