我有一个像这样的手写卷积函数:
void convolution(Mat &input, int size, int direction, Mat kernel, Mat &output) {
int kernelRadiusX = (kernel.size[0] - 1) / 2;
int kernelRadiusY = (kernel.size[1] - 1) / 2;
// Create padded version of input
Mat paddedInput;
copyMakeBorder(input, paddedInput,
kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY,
BORDER_REPLICATE);
GaussianBlur(paddedInput, paddedInput, Size(3, 3), 0, 0, BORDER_DEFAULT);
for (int i = 0; i < input.rows; i++) {
for (int j = 0; j < input.cols; j++) {
double sum = 0.0;
for (int m = -kernelRadiusX; m <= kernelRadiusX; m++) {
for (int n = -kernelRadiusY; n <= kernelRadiusY; n++) {
// find the correct indices we are using
int imagex = i + m + kernelRadiusX;
int imagey = j + n + kernelRadiusY;
int kernelx = m + kernelRadiusX;
int kernely = n + kernelRadiusY;
// get the values from the padded image and the kernel
int imageval = (int) paddedInput.at<uchar>(imagex, imagey);
double kernalval = kernel.at<double>(kernelx, kernely);
// do the multiplication
sum += imageval * kernalval;
}
}
output.at<double>(i, j) = sum;
}
}
}
我不确定这是否正确;我可以使用可以实现相同效果的opencv函数吗?我看过filter2D
,但它提供的输出与此函数的功能不同。
答案 0 :(得分:0)
您的代码几乎是准确的。您只是错过了导致不同结果的几件事:
natsort($array);
的调用,因此实际上对图像进行了二次卷积cv::GaussianBlur(...)
默认使用另一种图像边框类型this gist的OpenCV实现是正确的并且非常有效,更不用说支持的各种平台和体系结构(CPU,GPU)和并行化了。实际上,这是输入图像与内核的互相关(就信号处理而言,但在图像处理领域中通常称为卷积)。 可以实现比OpenCV更好的性能,但是使用的方法要复杂得多,所以我希望您的程序仅供学习使用。
我对您的功能做了一些改动。
cv::filter2D(...)
并检查我使用的输出是否相等:
//delete unused arguments
//add const where possible
//note another border type used by cv::filter2D
void convolution(const cv::Mat &input, cv::Mat &output, const cv::Mat & kernel, int borderType = cv::BORDER_REFLECT101)
{
// your function works only for single channel images
// and since you treat input pixels as uchar the only type supported is CV_8UC1
CV_Assert(input.type() == CV_8UC1);
CV_Assert(kernel.size[0] % 2 == 1);
CV_Assert(kernel.size[1] % 2 == 1);
// setting output image's type and size in processing function leads to clearer caller's code
// especially when output should match input
// that's why most of functions in OpenCV does so if possible
output = cv::Mat(input.size(), input.type());
int kernelRadiusX = (kernel.size[0] - 1) / 2;
int kernelRadiusY = (kernel.size[1] - 1) / 2;
// create padded version of input
cv::Mat paddedInput;
cv::copyMakeBorder(input, paddedInput,
kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY, borderType);
// actually it's not the fastest way to access pixel values
// but it's ok for ease of understanding (in study programm)
for (int i = 0; i < input.rows; i++)
{
for (int j = 0; j < input.cols; j++)
{
double sum = 0.0;
// addition of a kernel radius had no sense and decreased readability
for (int m = 0; m < kernel.size[0]; m++)
{
for (int n = 0; n < kernel.size[1]; n++)
{
// find the correct indices we are using
int imagex = i + m;
int imagey = j + n;
// get the values from the padded image and the kernel
int imageval = (int)paddedInput.at<uchar>(imagex, imagey);
double kernalval = kernel.at<double>(m, n);
// do the multiplication
sum += imageval * kernalval;
}
}
//trim values out of uchar's range
output.at<uchar>(i, j) = cv::saturate_cast<uchar>(sum);
}
}
}