C++?OpenCV實(shí)現(xiàn)boxfilter方框?yàn)V波的方法詳解
box filter簡(jiǎn)單解釋
box filter的作用很簡(jiǎn)單,即對(duì)局部區(qū)域求平均,并把值賦給某個(gè)點(diǎn),一般我們賦給區(qū)域中心。用公式表達(dá)如下:
其中patch是以(row,col)為中心的一塊區(qū)域。
為了跟后面的公式及程序?qū)?yīng),我們做如下定義:
- r:patch的半徑。半徑在寬高方向可以不相等,但是本文目的不在于對(duì)半徑的處理,所以簡(jiǎn)單起見(jiàn)設(shè)為相等。
- n:patch的長(zhǎng)度,等于(2∗r+1)。
- (rows,cols):圖像的尺寸,行數(shù)和列數(shù)。
- (row,col):對(duì)完整圖像的索引。
- (i,j):對(duì)圖像patch的索引
- k:對(duì)通道的索引。
1. 暴力實(shí)現(xiàn)——四循環(huán)
外層兩個(gè)循環(huán)是關(guān)于完整圖像(row,col)的循環(huán),內(nèi)層兩個(gè)循環(huán)是關(guān)于圖像patch(i,j)的循環(huán)。
注意:如果圖像是多通道的話實(shí)際上還有一個(gè)通常維度的循環(huán),但是通道數(shù)不是本文優(yōu)化的重心,所以本文不再贅述這個(gè)因素,后文也不再提,并且在計(jì)算量的估計(jì)中也會(huì)把這個(gè)因素省略掉。
這個(gè)實(shí)現(xiàn)比較簡(jiǎn)單,需要做的計(jì)算有:
- rows∗cols∗n∗n次加法,內(nèi)層循環(huán)的計(jì)算量o(n2),非常大。
- rows∗cols次除法:除法為了求平均
2. 行列分離
patch的平均可以進(jìn)行行列分離,也就是先對(duì)行方向做平均,并緩存結(jié)果,再對(duì)緩存的結(jié)果做列方向的平均。以公式的形式表達(dá)如下:
舉個(gè)例子展開(kāi)寫(xiě)會(huì)容易理解,比如3*3的patch,共9個(gè)數(shù):
這種方式的計(jì)算量:
- 2∗rows∗cols∗n次加法,相對(duì)于暴力版本,內(nèi)層循環(huán)降低了一個(gè)數(shù)量級(jí)的算力,變成o(n)了
- 2∗rows∗cols次除法
3. 行列分離優(yōu)化版
第二種實(shí)現(xiàn)可以對(duì)求和做進(jìn)一步優(yōu)化。在單個(gè)維度做求和時(shí),可以對(duì)當(dāng)前一維patch的和做一個(gè)緩存,當(dāng)中心點(diǎn)移動(dòng)后,減去彈出像素的值,加上新增像素的值,這樣就避免了重復(fù)性求和操作。
這種方案需要對(duì)patch的和做一個(gè)初始化和緩存,該方案的計(jì)算量為:
- 2∗rows∗cols次減法,2∗rows∗cols次加法,內(nèi)層循環(huán)的計(jì)算變?yōu)閛(1)了,進(jìn)一步降低了一個(gè)數(shù)量級(jí)算力。
- 2∗rows∗cols次除法
代碼
上面做計(jì)算量估計(jì)的時(shí)候沒(méi)有考慮邊界條件,在具體代碼實(shí)現(xiàn)的時(shí)候需要仔細(xì)處理邊界,防止數(shù)組訪問(wèn)越界。
代碼同時(shí)跟opencv做了個(gè)效果和性能的對(duì)比,第三種方式雖然仍然比opencv慢,但性能基本處于同一量級(jí)了,opencv可能還做了一些其他跟算法無(wú)關(guān)的優(yōu)化,比如指令集、并行化之類(lèi)的。
注意:下面為了方便比較,opencv boxFilter的邊界處理參數(shù)選擇BORDER_CONSTANT。即使是邊界處patch不滿覆蓋的情況下,opencv仍然除以n2 ,也就是說(shuō)除以的數(shù)字有點(diǎn)大了,所以邊界會(huì)逐漸發(fā)黑,特別是kernel_size(對(duì)應(yīng)于radius)比較大時(shí)候視覺(jué)效果更明顯。
#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; }
以上就是C++ OpenCV實(shí)現(xiàn)boxfilter方框?yàn)V波的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV boxfilter方框?yàn)V波的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++中關(guān)于[]靜態(tài)數(shù)組和new分配的動(dòng)態(tài)數(shù)組的區(qū)別分析
這篇文章主要介紹了C++中關(guān)于[]靜態(tài)數(shù)組和new分配的動(dòng)態(tài)數(shù)組的區(qū)別分析,很重要的概念,需要的朋友可以參考下2014-08-08C++實(shí)現(xiàn)編寫(xiě)二維碼的示例代碼
這篇文章主要為大家詳細(xì)介紹如何基于C++實(shí)現(xiàn)編寫(xiě)二維碼的功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-06-06詳解C語(yǔ)言處理算經(jīng)中著名問(wèn)題百錢(qián)百雞
古代的很多數(shù)學(xué)問(wèn)題都可以用現(xiàn)代的編程語(yǔ)言去嘗試解決,就如本篇,將會(huì)帶你通過(guò)C語(yǔ)言來(lái)解決算經(jīng)中百錢(qián)百雞問(wèn)題,感興趣的朋友來(lái)看看吧2022-02-02C++實(shí)現(xiàn)日期類(lèi)(Date類(lèi))的方法
下面小編就為大家?guī)?lái)一篇C++實(shí)現(xiàn)日期類(lèi)(Date類(lèi))的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01C++實(shí)現(xiàn)list增刪查改模擬的示例代碼
本文主要介紹了C++實(shí)現(xiàn)list增刪查改模擬,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12ubuntu 下編譯C++代碼出現(xiàn)的問(wèn)題解決
這篇文章主要介紹了ubuntu 下編譯C++代碼出現(xiàn)的問(wèn)題解決的相關(guān)資料,需要的朋友可以參考下2015-03-03