filter2D 实现的差异
Differences in filter2D implementation
我试图实现 convolute2D
(OpenCV 中的filter2D
)并想出了以下代码。
Mat convolute2D(Mat image, double** kernel, int W){
Mat filtered_image = image.clone();
// find center position of kernel (half of kernel size)
int kCenterX = W / 2;
int kCenterY = W / 2;
int xx = 0;
int yy = 0;
cout << endl << "Performing convolution .." << endl;
cout << "Image Size : " << image.rows << ", " << image.cols <<endl;
for (int i = 0; i < image.rows; ++i){
for (int j = 0; j < image.cols; ++j){
for(int x = 0; x < W; ++x){
xx = W - 1 - x;
for(int y = 0; y < W; ++y){
yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
filtered_image.at<uchar>(Point(j, i)) += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
}
}
return filtered_image;
}
假设我们总是有一个方核。但我的结果与 filter2D
有很大不同。是因为可能溢出还是我的实现有问题?
谢谢
您的代码有两个问题:
在向输出图像添加值之前不要将输出图像设置为零。因此,您正在计算 "input + filtered input",而不仅仅是 "filtered input".
假设 kernel
的值非常小,"input pixel * kernel value" 可能会产生一个小数字,在写入 uchar
时向下舍入。为内核添加这些值中的每一个,你最终会得到一个太低的结果。
我建议您这样做:
double res = 0;
for(int x = 0; x < W; ++x){
int xx = W - 1 - x;
for(int y = 0; y < W; ++y){
int yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
res += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
filtered_image.at<uchar>(Point(j, i)) = res;
这一次解决了这两个问题。此外,这应该会更快一些,因为访问输出图像会产生一些开销。
为了更快的速度,请考虑检查越界读取(内部循环中的 if
)会显着降低您的代码速度,并且对于大多数像素(因为很少的像素)完全没有必要靠近图像边缘)。相反,您可以将循环拆分为 [0,kCenterX]
、[kCenterX,image.rows-kCenterX]
和 [image.rows-kCenterX,image.rows]
。中间的循环通常是迄今为止最大的循环,不需要检查越界读取。
并使用cv::saturate_cast来正确赋值给uchar,例如:
filtered_image.at<uchar>(Point(j, i)) = cv::saturate_cast<uchar>(res);
我试图实现 convolute2D
(OpenCV 中的filter2D
)并想出了以下代码。
Mat convolute2D(Mat image, double** kernel, int W){
Mat filtered_image = image.clone();
// find center position of kernel (half of kernel size)
int kCenterX = W / 2;
int kCenterY = W / 2;
int xx = 0;
int yy = 0;
cout << endl << "Performing convolution .." << endl;
cout << "Image Size : " << image.rows << ", " << image.cols <<endl;
for (int i = 0; i < image.rows; ++i){
for (int j = 0; j < image.cols; ++j){
for(int x = 0; x < W; ++x){
xx = W - 1 - x;
for(int y = 0; y < W; ++y){
yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
filtered_image.at<uchar>(Point(j, i)) += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
}
}
return filtered_image;
}
假设我们总是有一个方核。但我的结果与 filter2D
有很大不同。是因为可能溢出还是我的实现有问题?
谢谢
您的代码有两个问题:
在向输出图像添加值之前不要将输出图像设置为零。因此,您正在计算 "input + filtered input",而不仅仅是 "filtered input".
假设
kernel
的值非常小,"input pixel * kernel value" 可能会产生一个小数字,在写入uchar
时向下舍入。为内核添加这些值中的每一个,你最终会得到一个太低的结果。
我建议您这样做:
double res = 0;
for(int x = 0; x < W; ++x){
int xx = W - 1 - x;
for(int y = 0; y < W; ++y){
int yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
res += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
filtered_image.at<uchar>(Point(j, i)) = res;
这一次解决了这两个问题。此外,这应该会更快一些,因为访问输出图像会产生一些开销。
为了更快的速度,请考虑检查越界读取(内部循环中的 if
)会显着降低您的代码速度,并且对于大多数像素(因为很少的像素)完全没有必要靠近图像边缘)。相反,您可以将循环拆分为 [0,kCenterX]
、[kCenterX,image.rows-kCenterX]
和 [image.rows-kCenterX,image.rows]
。中间的循环通常是迄今为止最大的循环,不需要检查越界读取。
并使用cv::saturate_cast来正确赋值给uchar,例如:
filtered_image.at<uchar>(Point(j, i)) = cv::saturate_cast<uchar>(res);