網站首頁 編程語言 正文
box filter簡單解釋
box filter的作用很簡單,即對局部區域求平均,并把值賦給某個點,一般我們賦給區域中心。用公式表達如下:
其中patch是以(row,col)為中心的一塊區域。
為了跟后面的公式及程序對應,我們做如下定義:
- r:patch的半徑。半徑在寬高方向可以不相等,但是本文目的不在于對半徑的處理,所以簡單起見設為相等。
- n:patch的長度,等于(2?r+1)。
- (rows,cols):圖像的尺寸,行數和列數。
- (row,col):對完整圖像的索引。
- (i,j):對圖像patch的索引
- k:對通道的索引。
1. 暴力實現——四循環
外層兩個循環是關于完整圖像(row,col)的循環,內層兩個循環是關于圖像patch(i,j)的循環。
注意:如果圖像是多通道的話實際上還有一個通常維度的循環,但是通道數不是本文優化的重心,所以本文不再贅述這個因素,后文也不再提,并且在計算量的估計中也會把這個因素省略掉。
這個實現比較簡單,需要做的計算有:
- rows?cols?n?n次加法,內層循環的計算量o(n2),非常大。
- rows?cols次除法:除法為了求平均
2. 行列分離
patch的平均可以進行行列分離,也就是先對行方向做平均,并緩存結果,再對緩存的結果做列方向的平均。以公式的形式表達如下:
舉個例子展開寫會容易理解,比如3*3的patch,共9個數:
這種方式的計算量:
- 2?rows?cols?n次加法,相對于暴力版本,內層循環降低了一個數量級的算力,變成o(n)了
- 2?rows?cols次除法
3. 行列分離優化版
第二種實現可以對求和做進一步優化。在單個維度做求和時,可以對當前一維patch的和做一個緩存,當中心點移動后,減去彈出像素的值,加上新增像素的值,這樣就避免了重復性求和操作。
這種方案需要對patch的和做一個初始化和緩存,該方案的計算量為:
- 2?rows?cols次減法,2?rows?cols次加法,內層循環的計算變為o(1)了,進一步降低了一個數量級算力。
- 2?rows?cols次除法
代碼
上面做計算量估計的時候沒有考慮邊界條件,在具體代碼實現的時候需要仔細處理邊界,防止數組訪問越界。
代碼同時跟opencv做了個效果和性能的對比,第三種方式雖然仍然比opencv慢,但性能基本處于同一量級了,opencv可能還做了一些其他跟算法無關的優化,比如指令集、并行化之類的。
注意:下面為了方便比較,opencv boxFilter的邊界處理參數選擇BORDER_CONSTANT。即使是邊界處patch不滿覆蓋的情況下,opencv仍然除以n2?,也就是說除以的數字有點大了,所以邊界會逐漸發黑,特別是kernel_size(對應于radius)比較大時候視覺效果更明顯。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <ctime>
using namespace std;
using namespace cv;
Mat BoxFilter_1(const Mat& image, int radius);
Mat BoxFilter_2(const Mat& image, int radius);
Mat BoxFilter_3(const Mat& image, int radius);
int main()
{
clock_t time_beg;
clock_t time_end;
Mat image = imread("lena_std.bmp", IMREAD_UNCHANGED);
image.convertTo(image, CV_32FC3);
image /= 255.0f;
int radius = 9;
int ksize = radius * 2 + 1;
Mat image_box_filter_cv;
time_beg = clock();
boxFilter(image, image_box_filter_cv, -1, Size(ksize, ksize), Point(-1, -1), true, BORDER_CONSTANT);
time_end = clock();
cout << "box-filter-cv time cost: " << time_end - time_beg << endl;
Mat image_box_filter_1 = BoxFilter_1(image, radius);
Mat image_box_filter_2 = BoxFilter_2(image, radius);
Mat image_box_filter_3 = BoxFilter_3(image, radius);
namedWindow("original_image", 1);
imshow("original_image", image);
namedWindow("cv_box_filter", 1);
imshow("cv_box_filter", image_box_filter_cv);
namedWindow("box_filter-1", 1);
imshow("box_filter-1", image_box_filter_1);
namedWindow("box_filter-2", 1);
imshow("box_filter-2", image_box_filter_2);
namedWindow("box_filter-3", 1);
imshow("box_filter-3", image_box_filter_3);
Mat diff;
cv::absdiff(image_box_filter_2, image_box_filter_3, diff);
namedWindow("diff", 1);
imshow("diff", 50 * diff);
waitKey(0);
destroyAllWindows();
return 0;
}
Mat BoxFilter_1(const Mat& image, int radius)
{
int cols = image.cols;
int rows = image.rows;
int channels = image.channels();
int row_bound = rows - 1;
int col_bound = cols - 1;
Mat result(rows, cols, CV_32FC3);
clock_t time_beg;
clock_t time_end;
time_beg = clock();
for (int row = 0; row < rows; ++row) {
int row_beg = max(row - radius, 0);
int row_end = min(row + radius, row_bound);
for (int col = 0; col < cols; ++col) {
int col_beg = max(col - radius, 0);
int col_end = min(col + radius, col_bound);
vector<float> sums(channels, 0.0f);
int count = 0;
for (int i = row_beg; i <= row_end; ++i) {
for (int j = col_beg; j <= col_end; ++j) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += image.at<Vec3f>(i, j)[k];
}
}
}
for (int k = 0; k < channels; ++k) {
result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
// opencv BORDER_CONSTANT:
/*float COUNT = (float)(2 * radius + 1) * (2 * radius + 1);
result.at<Vec3f>(row, col)[k] = sums[k] / COUNT;*/
}
}
}
result = cv::max(cv::min(result, 1.0), 0.0);
time_end = clock();
cout << "box-filter-1 time cost: " << time_end - time_beg << endl;
return result;
}
Mat BoxFilter_2(const Mat& image, int radius)
{
int cols = image.cols;
int rows = image.rows;
int channels = image.channels();
int row_bound = rows - 1;
int col_bound = cols - 1;
Mat result(rows, cols, CV_32FC3);
clock_t time_beg;
clock_t time_end;
time_beg = clock();
// compute mean for row-wise
Mat row_result(rows, cols, CV_32FC3);
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
int col_beg = max(col - radius, 0);
int col_end = min(col + radius, col_bound);
vector<float> sums(channels, 0.0f);
int count = 0;
for (int j = col_beg; j <= col_end; ++j) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += image.at<Vec3f>(row, j)[k];
}
}
for (int k = 0; k < channels; ++k) {
row_result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
}
}
}
// compute mean for column-wise
for (int col = 0; col < cols; ++col) {
for (int row = 0; row < rows; ++row) {
int row_beg = max(row - radius, 0);
int row_end = min(row + radius, row_bound);
vector<float> sums(channels, 0.0f);
int count = 0;
for (int i = row_beg; i <= row_end; ++i) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += row_result.at<Vec3f>(i, col)[k];
}
}
for (int k = 0; k < channels; ++k) {
result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
}
}
}
result = cv::max(cv::min(result, 1.0), 0.0);
time_end = clock();
cout << "box-filter-2 time cost: " << time_end - time_beg << endl;
return result;
}
Mat BoxFilter_3(const Mat& image, int radius)
{
int cols = image.cols;
int rows = image.rows;
int channels = image.channels();
Mat result(rows, cols, CV_32FC3);
clock_t time_beg;
clock_t time_end;
time_beg = clock();
// compute mean for row-wise
Mat row_result(rows, cols, CV_32FC3);
for (int row = 0; row < rows; ++row) {
// initialize sums for row
vector<float> sums(channels, 0.0f);
int count = 0;
for (int col = 0; col < radius; ++col) {
if (col < cols) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += image.at<Vec3f>(row, col)[k];
}
}
}
// process row
for (int col = 0; col < cols; ++col) {
int left = col - radius - 1;
int right = col + radius;
if (left >= 0) {
count--;
for (int k = 0; k < channels; ++k) {
sums[k] -= image.at<Vec3f>(row, left)[k];
}
}
if (right < cols) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += image.at<Vec3f>(row, right)[k];
}
}
for (int k = 0; k < channels; ++k) {
row_result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
}
}
}
// compute mean for column-wise
for (int col = 0; col < cols; ++col) {
// initialize sums for column
vector<float> sums(channels, 0.0f);
int count = 0;
for (int row = 0; row < radius; ++row) {
if (row < rows) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += row_result.at<Vec3f>(row, col)[k];
}
}
}
// process column
for (int row = 0; row < rows; ++row) {
int up = row - radius - 1;
int down = row + radius;
if (up >= 0) {
count--;
for (int k = 0; k < channels; ++k) {
sums[k] -= row_result.at<Vec3f>(up, col)[k];
}
}
if (down < rows) {
count++;
for (int k = 0; k < channels; ++k) {
sums[k] += row_result.at<Vec3f>(down, col)[k];
}
}
for (int k = 0; k < channels; ++k) {
result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
}
}
}
result = cv::max(cv::min(result, 1.0), 0.0);
time_end = clock();
cout << "box-filter-3 time cost: " << time_end - time_beg << endl;
return result;
}
原文鏈接:https://blog.csdn.net/bby1987/article/details/127288299
相關推薦
- 2022-05-12 Kotlin any/none/all 函數
- 2022-11-22 新建的React?Native就遇到vscode報警解除方法_React
- 2022-04-09 Python調用win10toast框架實現定時調起系統通知_python
- 2023-02-06 shell腳本實戰之部署nginx腳本實例_nginx
- 2022-08-15 springboot項目搭建和基礎應用
- 2022-04-01 基于python,Matplotlib繪制函數的等高線與三維圖像_python
- 2023-06-16 Pytorch中的?torch.distributions庫詳解_python
- 2023-07-05 settings delete global hidden_api_policy_pre_p_app
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支