Skip to main content
Code Review

Return to Answer

Spelling fix and formatting
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322

(both versions compiled with GCC 7.1.0 with -O3 -march=native-O3 -march=native on an Intel i7-Q6700)

Note that my code doesn't initialize the kernel with zeroeszeros - it leaves it uninitialized, and instead writes to every element when it reaches it. This saves two passes over the memory. I precompute the denominator for the same reason, though that's only truly valid for a kernel of infinite size.

(both versions compiled with GCC 7.1.0 with -O3 -march=native on an Intel i7-Q6700)

Note that my code doesn't initialize the kernel with zeroes - it leaves it uninitialized, and instead writes to every element when it reaches it. This saves two passes over the memory. I precompute the denominator for the same reason, though that's only truly valid for a kernel of infinite size.

(both versions compiled with GCC 7.1.0 with -O3 -march=native on an Intel i7-Q6700)

Note that my code doesn't initialize the kernel with zeros - it leaves it uninitialized, and instead writes to every element when it reaches it. This saves two passes over the memory. I precompute the denominator for the same reason, though that's only truly valid for a kernel of infinite size.

Show the OpenCV way of doing it
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322

This is also shorter, and arguably easier on the eye. In this version, I show an alternative to precomputing the denominator - use std::accumulate to add it together afterwards.


The simplest and fastest version, though, is to use the Gaussian distribution provided by OpenCV, and just matrix-multiply the two together:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 auto gauss_x = cv::getGaussianKernel(cols, sigmax, CV_32F);
 auto gauss_y = cv::getGaussianKernel(rows, sigmay, CV_32F);
 return gauss_x * gauss_y.t();
}

Do note that if you intend to use the kernel for filtering, it's best to keep it separated, and perform horizontal and vertical passes each with a one-dimensional kernel.

This is also shorter, and arguably easier on the eye.

This is also shorter, and arguably easier on the eye. In this version, I show an alternative to precomputing the denominator - use std::accumulate to add it together afterwards.


The simplest and fastest version, though, is to use the Gaussian distribution provided by OpenCV, and just matrix-multiply the two together:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 auto gauss_x = cv::getGaussianKernel(cols, sigmax, CV_32F);
 auto gauss_y = cv::getGaussianKernel(rows, sigmay, CV_32F);
 return gauss_x * gauss_y.t();
}

Do note that if you intend to use the kernel for filtering, it's best to keep it separated, and perform horizontal and vertical passes each with a one-dimensional kernel.

I had missed the scaling of the x² and y² terms - oops! And some other bugs identified by asker
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322
#include <opencv2/core/core.hpp>
#include <cmath>
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 const auto y_mid = (rows-1) / 2.0;
 const auto x_mid = (cols-1) / 2.0;
 
 const auto x_spread = 1. / (sigmax*sigmax*2);
 const auto y_spread = 1. / (sigmay*sigmay*2);
 const auto denominator = 8 * std::atan(1) * sigmax * sigmay;
 std::vector<double> gauss_x, gauss_y;
 gauss_x.reserve(cols);
 for (auto i = 0; i < cols; ++i) {
 auto x = i - x_mid;
 gauss_x.push_back(std::exp(-x*x * x_spread));
 }
 gauss_y.reserve(rows);
 for (auto i = 0; i < rows; ++i) {
 auto y = i - y_mid;
 gauss_y.push_back(std::exp(-y*y * y_spread));
 }
 cv::Mat kernel = cv::Mat::zeros(rows, cols, CV_32FC1);
 for (auto j = 0; j < rows; ++j)
 for (auto i = 0; i < rows;cols; ++i) {
 kernel.at<float>(j,i) = gauss_x[i] * gauss_y[j] / denominator;
 }
 return kernel;
}
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 auto gauss_x = cv::Mat_<float>(rowscols, 1);
 const auto x_mid = (cols-1) / 2.0;
 const auto y_mid = (rows-1) / 2.0;
 const auto denominatorx_spread = 81. */ atan(1sigmax*sigmax*2);
 * sigmax * sigmay;const auto y_spread = 1. / (sigmay*sigmay*2);
 for (auto i = 0; i < cols; ++i) {
 auto const x = i - x_mid;
 gauss_x[i][0] = std::exp(-x*x * x_spread);
 }
 auto kernel = cv::Mat_<float>(rows, cols);
 for (auto i = 0; i < rows; ++i) {
 auto const y = i - y_mid;
 kernel.row(i) = gauss_x * (std::exp(-y*y)/denominator * y_spread);
 }
 const auto denominator = std::accumulate(kernel.begin(), kernel.end(), 0);
 return kernel;kernel / denominator;
}
#include <opencv2/core/core.hpp>
#include <cmath>
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 const auto y_mid = (rows-1) / 2.0;
 const auto x_mid = (cols-1) / 2.0;
 const auto denominator = 8 * std::atan(1) * sigmax * sigmay;
 std::vector<double> gauss_x, gauss_y;
 gauss_x.reserve(cols);
 for (auto i = 0; i < cols; ++i) {
 auto x = i - x_mid;
 gauss_x.push_back(std::exp(-x*x));
 }
 gauss_y.reserve(rows);
 for (auto i = 0; i < rows; ++i) {
 auto y = i - y_mid;
 gauss_y.push_back(std::exp(-y*y));
 }
 cv::Mat kernel = cv::Mat::zeros(rows, cols, CV_32FC1);
 for (auto j = 0; j < rows; ++j)
 for (auto i = 0; i < rows; ++i) {
 kernel.at<float>(j,i) = gauss_x[i] * gauss_y[j] / denominator;
 }
 return kernel;
}
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 auto gauss_x = cv::Mat_<float>(rows, 1);
 const auto x_mid = (cols-1) / 2.0;
 const auto y_mid = (rows-1) / 2.0;
 const auto denominator = 8 * atan(1) * sigmax * sigmay;
 for (auto i = 0; i < cols; ++i) {
 auto const x = i - x_mid;
 gauss_x[i][0] = std::exp(-x*x);
 }
 auto kernel = cv::Mat_<float>(rows, cols);
 for (auto i = 0; i < rows; ++i) {
 auto const y = i - y_mid;
 kernel.row(i) = gauss_x * (std::exp(-y*y)/denominator);
 }
 return kernel;
}
#include <opencv2/core/core.hpp>
#include <cmath>
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 const auto y_mid = (rows-1) / 2.0;
 const auto x_mid = (cols-1) / 2.0;
 
 const auto x_spread = 1. / (sigmax*sigmax*2);
 const auto y_spread = 1. / (sigmay*sigmay*2);
 const auto denominator = 8 * std::atan(1) * sigmax * sigmay;
 std::vector<double> gauss_x, gauss_y;
 gauss_x.reserve(cols);
 for (auto i = 0; i < cols; ++i) {
 auto x = i - x_mid;
 gauss_x.push_back(std::exp(-x*x * x_spread));
 }
 gauss_y.reserve(rows);
 for (auto i = 0; i < rows; ++i) {
 auto y = i - y_mid;
 gauss_y.push_back(std::exp(-y*y * y_spread));
 }
 cv::Mat kernel = cv::Mat::zeros(rows, cols, CV_32FC1);
 for (auto j = 0; j < rows; ++j)
 for (auto i = 0; i < cols; ++i) {
 kernel.at<float>(j,i) = gauss_x[i] * gauss_y[j] / denominator;
 }
 return kernel;
}
cv::Mat getGaussianKernel(int rows, int cols, double sigmax, double sigmay)
{
 auto gauss_x = cv::Mat_<float>(cols, 1);
 const auto x_mid = (cols-1) / 2.0;
 const auto y_mid = (rows-1) / 2.0;
 const auto x_spread = 1. / (sigmax*sigmax*2);
 const auto y_spread = 1. / (sigmay*sigmay*2);
 for (auto i = 0; i < cols; ++i) {
 auto const x = i - x_mid;
 gauss_x[i][0] = std::exp(-x*x * x_spread);
 }
 auto kernel = cv::Mat_<float>(rows, cols);
 for (auto i = 0; i < rows; ++i) {
 auto const y = i - y_mid;
 kernel.row(i) = gauss_x * std::exp(-y*y * y_spread);
 }
 const auto denominator = std::accumulate(kernel.begin(), kernel.end(), 0);
 return kernel / denominator;
}
Row-at-a-time using OpenCV
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322
Loading
atan(1) is π/4, not π
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322
Loading
Source Link
Toby Speight
  • 87.3k
  • 14
  • 104
  • 322
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /