opencv 的norm_22、OpenCV用卷积Filter2D进行滤波器
迄今為止,看到的函數中,卷積的操作發生在OpenCV函數的內部。理論上,圖像卷積就是將內核與圖像覆蓋區域對應位置相乘之后求和。從調用函數上來看,它需要一個數組參數來描述內核。在實踐層面,有一個重要的微妙因素會對結果產生重大影響。微妙之處在于一些內核是可分離的,而另一些則不是。
圖1圖1(A)是可分離的; 它可以表示為兩個一維卷積(B和C);D是一個不可分割內核的例子。可分離的內核是可以被認為是兩個一維內核的內核,首先與x內核進行卷積然后與y內核進行卷積來應用。這種分解的好處是內核卷積的計算成本大約是圖像面積乘以內核區域。這意味著用n×n內核卷積區域A的圖像需要時間與An2成正比,同時n×1內核與圖像卷積一次,然后與1×n內核卷積占用與An + An = 2An成比例。即使n小于3也有好處,隨著n的增長,優勢更為突出。
1、 利用filter2D()濾波
鑒于圖像卷積所需的操作次數,是圖像中像素的數量乘以內核中的像素數,這可能需要很多計算,因此,在這種情況下,最好讓OpenCV來幫你完成并利用內部優化。OpenCV完成這些操作的函數是filter2D():
cv::filter2D(
cv::InputArray src,
cv::OutputArray dst,
int ddepth,
cv::InputArray kernel,
cv::Point anchor = cv::Point(-1,-1),
double delta = 0,
int borderType = cv::BORDER_DEFAULT
);
創建一個適當大小的數組,并用濾波器的系數填充它,然后將它與源圖像和目標圖像一起傳遞到filter2D()中。可以使用ddepth指定結果圖像的深度,使用錨點指定濾波的錨點,使用borderType指定邊界類型。如果定義了錨點,則內核可以是偶數大小;否則,它應該是奇數大小。如果要在應用濾波器后將總體偏移應用于結果,則可以使用參數delta。
2、 利用sepFilter2D分離濾波器
在內核可分離的情況下,通過以分離的形式表示并將這些一維內核傳遞給OpenCV,將從OpenCV獲得最佳性能。sepFilter2D()與filter2D()類似,除了它期望這兩個一維內核而不是一個二維內核。
cv::sepFilter2D(
cv::InputArray src,
cv::OutputArray dst,
int ddepth,
cv::InputArray rowKernel,
cv::InputArray columnKernel,
cv::Point anchor = cv::Point(-1,-1),
double delta = 0,
int borderType = cv::BORDER_DEFAULT
);
sepFilter2D()的所有參數都與cv :: filter2D()的參數相同,但使用rowKernel和columnKernel參數替換內核參數除外。后者為n1×1和1×n2陣列,n1不一定等于n2。
3、構建內核
getDerivKernel()構造Sobel和Scharr內核,getGaussianKernel()構造高斯內核。
void cv::getDerivKernels(
cv::OutputArray kx,
cv::OutputArray ky,
int dx,
int dy,
int ksize,
bool normalize = true,
int ktype = CV_32F
);
getDerivKernel()的結果放置在kx和ky參數中。內核(Sobel和Scharr)是可分離的內核,將返回兩個數組,一個是1×ksize(行系數,kx),另一個是ksize×1(列系數,ky)。這些是從x和y導數階dx和dy計算而來的。衍生內核總是方形的,因此大小參數ksize是一個整數。ksize可以是1,3,5,7或cv ::SCHARR中的任何一個。normalize參數告訴getDerivKernels()是否應該對內核元素進行歸一化。對于在浮點圖像上操作的情況,將normalize設置為true,但是當正在操作整數數組時,通常更為明智的做法是,在一些數組之前不對數組進行歸一化,這樣就不會丟掉以后需要的精度。最后一個參數ktype表示濾波器系數的類型。 ktype的值可以是CV_32F或CV_64F。
高斯濾波器的實際內核數組由getGaussianKernel()生成。
cv::Mat cv::getGaussianKernel(
int ksize, // Kernel size
double sigma, // Gaussian half-width
int ktype = CV_32F // Type for filter coefficients
);
與派生內核一樣,高斯內核是可分離的。因此,getGaussianKernel()只計算一個ksize×1的系數數組。ksize的值可以是任何奇數正數。參數sigma設置近似高斯分布的標準偏差。根據以下函數從sigma計算系數:
也就是說,計算系數α使得濾波器整體被歸一化。可以將其設置為-1,在這種情況下,將根據ksize大小自動計算σ值。在這種情況下,
例1是具體的使用方法,從中能看出構造的每個卷積核的用途嗎?
例1:filter2D、sepFilter2D、getDerivKernels、getGaussianKernel功能演示。
#include <iostream> #include <opencv2opencv.hpp> using namespace std; using namespace cv; int main(int argc, char** argv) {Mat src = imread("E:/ img.bmp", 1);namedWindow("原始噪聲圖像", 0);imshow("原始噪聲圖像", src);//構造濾波器Mat kernal = Mat::ones(3, 3, CV_32FC1);kernal/= 9;Mat filterDst;filter2D(src, filterDst, src.depth(), kernal);namedWindow("filter2D結果1", 0);imshow("filter2D結果1", filterDst);Mat kernal2 = (Mat_<char>(3, 3) << 0, -1, 0,-1, 5, -1,0, -1, 0);Mat dst1;filter2D(filterDst, dst1, src.depth(), kernal2);namedWindow("filter2D結果2", 0);imshow("filter2D結果2", dst1);Mat kx = (Mat_<float>(1, 3) << 0, -1, 0);Mat ky = (Mat_<float>(1, 3) << -1, 0, -1);sepFilter2D(filterDst, dst1, src.depth(), kx, ky);// , Point(-1, -1), 0, BORDER_DEFAULT);namedWindow("sepFilter2D結果", 0);imshow("sepFilter2D結果", dst1);getDerivKernels(kx, ky, 1, 1, 3, true);cout << kx << endl;cout << ky << endl;Mat dst2;sepFilter2D(filterDst, dst2, src.depth(), kx, ky);// , Point(-1, -1), 0, BORDER_DEFAULT);cv::normalize(dst2, dst2, 0, 255, NORM_MINMAX, CV_8UC1);namedWindow("sepFilter2D結果2", 0);imshow("sepFilter2D結果2", dst2);Mat gaussKernal;gaussKernal = getGaussianKernel(7, -1);filter2D(src, filterDst, src.depth(), gaussKernal);namedWindow("Filter2D結果3", 0);imshow("Filter2D結果3", filterDst);waitKey(0);return 0; }圖2 操作結果示意圖 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的opencv 的norm_22、OpenCV用卷积Filter2D进行滤波器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python网络库_python的网络库
- 下一篇: 华为鸿蒙系统是指芯片吗_华为首部鸿蒙手机