日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV的滤波与卷积

發布時間:2024/7/23 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV的滤波与卷积 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

預備知識

濾波、核和卷積

邊界外推和邊界處理

閾值化操作

Otsu算法

自適應閾值

平滑

簡單模糊和方框型濾波器

中值濾波器

高斯濾波器

雙邊濾波器

導數和梯度

索貝爾導數

Scharr濾波器

拉普拉斯變換

圖像形態學

膨脹和腐蝕

通用形態學函數

開操作和閉操作

形態學梯度

頂帽和黑帽

自定義核

用任意線性濾波器做卷積

用cv::filter2D()進行卷積

通過cv::sepFilter2D使用可分核

生成卷積核


預備知識

濾波、核和卷積

濾波器指的是一種由一幅圖像 I(x,y)根據像素點x,y附近的區域計算得到一幅新圖像 I'(x,y)的算法。其中,模板規定了濾波器的形狀以及這個區域內像素的值的組成規律,也稱“濾波器”或“核”。在下面的介紹中多采用的是線性核,即 I'(x,y)的像素的值由 I(x,y)及其周圍的像素的值的加權相加得來的。可由以下方程表示:

(A)5×5盒狀核(B)規范化的5×5盒狀核(C)3×3的Sobel核(D)5×5規范化高斯核

注:“錨點”均用粗體表示

邊界外推和邊界處理

自定義邊框

在處理圖像時,只要告訴調用的函數添加虛擬像素的規則,庫函數就會自動創建虛擬像素。cv::copyMakeBorder()就是一個為圖像創建邊框的函數。

cv::copyMakeBorder(InputArray src, InputArray dst, int top, int bottom, int left, int right, int borderType, const cv::Scalar& value=cv::Scalar())

作用:通過指定兩幅圖像,同時指明填充方法,該函數就會將第一幅圖填補后的結果保存在第二幅圖像中。其中,src是原圖像,dst是填充后的圖像,top、bottom、left、right分別是四個方向上的尺寸,borderType是像素填充的方式,value是常量填充時的值。

borderType的取值及效果取值效果
cv::BORDER_CONSTANT為每個邊框像素賦予一個相同的值。
cv::BORDER_WRAP類似于平鋪擴充
cv::BORDER_REPLICATE復制邊緣的像素擴充
cv::BORDER_REFLECT通過鏡像復制擴充
cv::BORDER_REFLECT_101通過鏡像復制擴充,邊界像素除外
cv::BORDER_DEFAULTcv::BORDER_REFLECT_101

自定義外推

?int cv::borderInterpolate(int p, int len, int borderType)

作用:計算一個維度上的外推,p為原圖上一個坐標,len是p指維度上的大小,borderType是邊界類型。

例子:混合的邊界條件下計算一個特定像素的值,在一維中使用BORDER_REFLECT_101,在二維中使用BORDER_WRAP:

float val = img.at<float>(cv::borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101),cv::borderInterpolate(-5, img.cols, cv::BORDER_WRAP) )

閾值化操作

閾值化操作的原理是對于數組中每個值,根據其高于或低于某個閾值做出相對應的處理,OpenCV中提供了實現這種功能的方法cv::threshold()。

double?cv::threshold(InputArray src, cv::OutputArray dst, double thresh, double maxValue, int thresholdType)

thresholdType的可選項及其操作閾值類型操作
cv::THRESH_BINARY
cv::THRESH_BINARY_INV
cv::THRESH_TRUNC
cv::THRESH_TOZERO
cv::THRESH_TOZERO_INV
不同閾值類型對應的結果

?

例一:將一幅圖像的三個通道相加并將像素值限制在100以內

#include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream>using namespace std;void sum_rgb(const cv::Mat& src, cv::Mat& dst) {// 分通道vector<cv::Mat> planes;cv::split(src, planes);cv::Mat b = planes[0];cv::Mat g = planes[1];cv::Mat r = planes[2];cv::Mat s;// 加權融合,防止越界cv::addWeighted(r, 1. / 3, g, 1. / 3, 0.0, s);cv::addWeighted(s, 1., r, 1. / 3, 0.0, s);// 閾值化操作cv::threshold(s, dst, 100, 100, cv::THRESH_TRUNC); }void help() {cout << "Call: ./ch10_ex10_1 faceScene.jpg" << endl;cout << "Show use of alpha blending (addWeighted) and threshold" << endl; }int main() {help();cv::Mat src = cv::imread("D:\\personal-data\\wallpapers\\test.png");cv::Mat dst;if (src.empty()){cout << "can not load the image" << endl;return -1;}sum_rgb(src, dst);cv::imshow("img", dst);cv::waitKey(0);cv::destroyAllWindows();return 0; }

運行結果:?

例二:對于浮點型圖像進行三個通道相加并將像素值限制在100以內

#include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream>using namespace std;void sum_rgb(const cv::Mat& src, cv::Mat& dst) {// 分通道vector<cv::Mat> planes;cv::split(src, planes);cv::Mat b = planes[0];cv::Mat g = planes[1];cv::Mat r = planes[2];// 全0初始化s矩陣cv::Mat s = cv::Mat::zeros(b.size(), CV_32F);// accumulate可將8位整型的圖像累加到一幅浮點型的圖像中cv::accumulate(b, s);cv::accumulate(g, s);cv::accumulate(r, s);// 閾值化操作cv::threshold(s, dst, 100, 100, cv::THRESH_TRUNC);s.convertTo(dst, b.type()); }void help() {cout << "Call: ./ch10_ex10_1 faceScene.jpg" << endl;cout << "Show use of alpha blending (addWeighted) and threshold" << endl; }int main() {help();cv::Mat src = cv::imread("D:\\personal-data\\wallpapers\\test.png");cv::Mat dst;if (src.empty()){cout << "can not load the image" << endl;return -1;}sum_rgb(src, dst);cv::imshow("img", dst);cv::waitKey(0);cv::destroyAllWindows();return 0; }

運行結果:

Otsu算法

函數cv::threshold()也可以自動決定最優的閾值,只需將參數thresh傳遞值cv::THRESH_OTSU即可。

簡而言之,Otsu算法就是遍歷所有可能的閾值,然后對每個閾值結果的兩類像素(低于閾值和高于閾值兩類像素)計算方差,然后計算的值,取其最小的閾值。

式中,和是根據兩類像素的數量計算而來的權重。由于要遍歷所有可能的閾值,所以這并不是一個相對高效的過程。

自適應閾值

自適應閾值方法中閾值在整個過程中自動產生變化,這由OpenCV中的cv::adaptiveThreshold()實現。

void?cv::adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int threshType, int blockSize, double C)

該方法是逐個像素地計算自適應閾值T(x, y),具體計算過程是:計算每個像素位置周圍的blockSize×blockSize區域的加權平均值,然后減去常數C。求均值時所用權重和adaptiveMethod有關,若是cv::ADAPTIVE_THRESH_MEAN_C,則權重相等,若是cv::ADAPTIVE_THRESH_GAUSSIAN_C,則權重由高斯方差得到。

注:該方法只適應與單通道8位或浮點型圖像

#include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream>using namespace std;int main() {// 設置參數double fixed_threshold = 15;int threshold_type = 1 ? cv::THRESH_BINARY : cv::THRESH_BINARY_INV;int adaptive_method = 1 ? cv::ADAPTIVE_THRESH_MEAN_C : cv::ADAPTIVE_THRESH_GAUSSIAN_C;int block_size = 71;double offset = 15;// 以灰度圖形式加載圖片cv::Mat Igray = cv::imread("D:\\personal-data\\wallpapers\\test.png", cv::IMREAD_GRAYSCALE);// 判斷圖像是否加載成功if (Igray.empty()) { cout << "Can not load " << "D:\\personal-data\\wallpapers\\test.png" << endl; return -1; }// 聲明輸出矩陣cv::Mat It, Iat;// 閾值化操作cv::threshold(Igray,It,fixed_threshold,255,threshold_type);// 自適應閾值cv::adaptiveThreshold(Igray,Iat,255,adaptive_method,threshold_type,block_size,offset);// 展示結果圖像cv::imshow("Raw", Igray);cv::imshow("Threshold", It);cv::imshow("Adaptive Threshold", Iat);cv::waitKey(0);cv::destroyAllWindows();return 0; }

運行結果:

當threshold_type=1且adaptive_method=1

原圖

閾值化操作

自適應閾值化

當threshold_type=0且adaptive_method=0

原圖

閾值化操作

自適應閾值化

平滑

平滑也稱“模糊”,是一種簡單而又常用的圖像處理操作。平滑圖像的目的有很多,但通常都是為了減少噪聲和偽影。在降低圖像分辨率的時候,平滑也十分重要,可以防止圖片出現鋸齒狀。

簡單模糊和方框型濾波器

void cv::blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT)

作用:實現簡單的濾波,目標圖像中的每個值都是原圖像中相應位置一個窗口(核)中像素的平均值,窗口的尺寸由ksize聲明;anchaor指定計算時核與源圖像的對齊方式,默認情況下anchor為cv::Point(-1, -1),表示核相對于濾波器居中。

void cv::boxFilter(InputArray src, OutputArray dst, cv::Size ksize, cv::Point anchor=cv::Point(-1,-1), bool normalize=true;? int borderType=cv::BORDER_DEFAULT)

作用:方框濾波器是一種矩形的并且濾波器中所有值全部相等的濾波器。通常,所有的為1或者1/A,其中A是濾波器的面積。后一種濾波器稱為“歸一化方框型濾波器”,下面所示的是一個5×5的模糊濾波器,也稱“歸一化方框型濾波器”。

通過上述介紹,可以發現cv::boxFilter()是一種一般化的形式,而cv::blur()是一種特殊化的形式。但前者可以以非歸一化形式調用,并且輸出圖像深度可以控制,但后者智能以歸一化形式調用,且輸出圖像深度必須和原圖像保持一致。

中值濾波器

中值濾波器(Median Filter)將每個像素替換為圍繞這個像素的矩形領域內的中值或“中值”像素(相對于平均像素)。通過均值濾波器對噪聲圖像,尤其是有較大孤立的異常值非常敏感,少量具有較大偏差的點也會嚴重影響到均值濾波器。中值濾波器可以采用取其中間點的方式來消除異常值。

void cv::medianBlur(InputArray src,?OutputArray dst, Size ksize)

高斯濾波器

關于高斯濾波器,在之前的文章中已做了詳細介紹,可以參考OpenCV高斯濾波GaussianBlur

雙邊濾波器

雙邊濾波器是一種比較大的圖像分析算子,也就是邊緣保持平滑。高斯平滑的模糊過程是減緩像素在空間上的變化,因此與鄰近的關系緊密,而隨機噪聲在像素間的變化幅度又會非常的大(即噪聲不是空間相關的)。基于這種前提高斯平滑很好地減弱了噪聲并且保留了小信號,但是卻破壞了邊緣信息,邊緣也模糊了。

和高斯平滑類似,雙邊濾波對每個像素及其領域內的像素進行了加權平均。其權重由兩部分組成,第一部分同高斯平滑,第二部分也是高斯權重,但是它不是基于空間距離而是色彩強度差計算而來的,在多通道(色彩)圖像上強度差由各分量的加權累加代替。可將其當做高斯平滑,指示相似程度更高的像素權值更高,邊緣更加明顯,對比度更高。

cv::bilateralFilter(InputArray src,?OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType= cv::BORDER_DEFAULT)

參數:d是像素鄰近的最大距離,處理視頻時一般不大于5,非實時應用時可放大到9;sigmaColor是色彩空間濾波器的sigma值,該值越大,則色彩強度越大,不連續性越強;sigmaSpace是坐標空間濾波器的sigma值。

導數和梯度

卷積中最重要也是最基本的部分就是(近似)計算導數。

索貝爾導數

一般來說,用來表示微分的最常用的算子是索貝爾(Sobel)導數算子,可以實現任意階導數和混合偏導數。

void sv::Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, cv::Size ksize=3, double scale=1, double delta=0, int borderType=cv::BORDER_DEFAULT)

參數:ddepth指明目標圖像的深度或類型;xorder和yorder是求導順序,其取值可為0,、1或2,其中0代表在該方向不求導,kszie是一個奇數,表示濾波器核的大小, 目前最大到31;閾值和偏移量在結果存入dst前進行調用,公式如下:

Sobel算子的好處是可以將核定義為各種大小,并且可以快速迭代式地構造這些核。大的核可以更好地近似導數,因為可以消除噪聲影響。其缺點是如果導數在空間上變化劇烈,核太大會使結果發生偏差,并且核比較小時準確度不高。

實際上,由于Sobel算子定義在離散空間上,所以它并不是真正的導數,而是一個多項式,即在x方向上進行Sobel運算表示的并不是二階導數,而是對拋物線函數的局部擬合。

Scharr濾波器

為了將圖像內的信息聯系起來,可能需要測量一幅圖像:在處理過程中,通過在目標附近組織一幅梯度直方圖來收集其形狀信息,這些直方圖是許多形狀分類器訓練和使用的基礎。因此,梯度角的誤差會降低分類器識別的效果。

對于3×3的Sobel濾波器,梯度角距離水平或垂直方向越遠,誤差越明顯。在OpenCV中,調用cv::Sobel()時設置ksize為cv::SCHARR,即可消除3×3這樣小但是快的Sobel導數濾波器所帶來的誤差。Scharr濾波器核Sobel濾波器同樣很快,但是前者精度更高。因此選擇3×3的濾波器時,應當使用Scharr濾波器。

? ? ?或? ? ?

拉普拉斯變換

OpenCV中的函數Laplacian實現了對拉普拉斯算子的離散近似:

void sv::Laplacian(InputArray src, OutputArray dst, int ddepth, cv::Size ksize=3, double scale=1, double delta=0, int borderType=cv::BORDER_DEFAULT)

只要ksize不為1,Laplacian算子的實現就是直接計算Sobel算子響應之和。當ksize=1時的卷積核如下所示:

Laplacian算子可應用于各種場景處理,一種常見的應用就是匹配“斑點”。Laplacian算子就是圖像在x和y軸方向的導數之和,這意味著一個被較大值包圍的點或小斑點(比ksize小)處的值將會變得很大。相反,被較小值包圍的點或小斑點處的值將在負方向上變得很大。

Laplacian算子同樣可以用于邊緣檢測,函數一階導數在原函數變化大的地方,值會相應變大,同樣在圖像邊緣處也同樣變化,所有導數在這些地方將變得很大。因此可以在二階導數為0的地方搜尋這么一個極大值,原圖像中的邊緣通過Laplacian算子運算后會變成0。對于有些不是邊緣也變成0的問題,可以通過濾掉Sobel一階導數中較大值的點解決。

圖像形態學

OpenCV提供了一種高效且易用的圖像形態學變換接口。其中有很多形態學方法,但基本上所有的形態學操作都基于兩種原始操作——膨脹與腐蝕。

膨脹和腐蝕

膨脹和腐蝕是最基本的形態學變換,可用于消除噪聲、元素分割和連接等。基于這兩種操作,可以實現更復雜的形態學操作,用來定位強度峰值或孔洞、另一種形式的圖像梯度等。

膨脹是一種卷積操作,它將目標像素的值替換為卷積核覆蓋區域的局部最大值,擴張了明亮區域,填充凹面。此卷積核是一個非線性核,是一個四邊形或圓形的實心核,其錨點在中心。與膨脹對應,腐蝕是與之相反的操作,腐蝕操作計算的是核覆蓋范圍內的局部最小值縮減了明亮區,消除凸起。

原圖

膨脹

腐蝕

void cv::erode(InputArray src, OutputArray dst, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1?int borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

void cv::dilate(InputArray src, OutputArray dst, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1?int borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

以上兩個函數分別是膨脹和腐蝕對應的函數,第三個參數是核,可以傳遞一個未初始化的cv::Mat,會使用默認的錨點在中心的3×3的核。

腐蝕操作通常用于消除圖中斑點一樣的噪聲,原理是斑點經過腐蝕后會消失,而大的可見區域不會受影響。膨脹操作通常用于發生連通分支。

通用形態學函數

當處理的對象是二值圖像時,像素只能是開(>0)或關(=0)的圖像掩膜時,基本的腐蝕和膨脹操作就夠用了。需要對灰度圖或者彩色圖進行處理時,一些其他操作就非常有用了,這些操作可以通過cv::morphologyEx()實現。

void cv::morphologyEx(InputArray src, OutputArray dst, int op, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1, int?borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

cv::morphologyEx()操作op選項操作值形態學操作是否需要臨時圖像
cv::MORPH_OPEN開操作
cv::MORPH_CLOSE閉操作
cv::MORPH_GRADIENT形態學梯度總是需要
cv::MORPH_TOPHAT頂帽操作就地調用需要(src = dst)
cv::MORPH_BLACKHAT地貌操作就地調用需要(src = dst)

開操作和閉操作

開操作先將圖像進行腐蝕,然后對腐蝕的結果進行膨脹。開操作常用語對二值圖像中的區域進行計算。

閉操作想將圖像進行膨脹,然后對膨脹的結果進行腐蝕。閉操作用于復雜連通分支算法中減少無用或噪聲驅動的片段。

對于連通分支,通常先進行腐蝕或閉操作消除噪聲,然后通過開操作連接相互靠近的大型區域。

對于一幅非布爾型圖像進行形態學操作時,閉操作最明顯的效果是消除值小于鄰域內的點的孤立異常,而開操作消除的是大于鄰域內點的孤立異常值。

形態學梯度

梯度操作的結果(擴張亮域)減腐蝕操作的結果(縮減亮域)產生了原圖像中的目標邊緣。對于灰度圖像,其結果就是計算明暗變換的趨勢。形態學梯度通常用于顯示明亮區域的邊界,然后便可以將他們看作目標或者目標的部分。用擴張的圖像減去了收縮的圖像便得到完整的邊界。與計算梯度不同,它并不會關注某個物體的周圍。

頂帽和黑帽

頂帽用于顯示與其鄰近相比更亮的部分;黑帽用于顯示與其鄰近相比更暗的部分。

#include "stdafx.h" #include <opencv2/opencv.hpp>int main() {cv::namedWindow("image", cv::WINDOW_NORMAL);cv::namedWindow("erosion", cv::WINDOW_NORMAL);cv::namedWindow("dilation", cv::WINDOW_NORMAL);cv::namedWindow("opening", cv::WINDOW_NORMAL);cv::namedWindow("closing", cv::WINDOW_NORMAL);cv::namedWindow("gradient", cv::WINDOW_NORMAL);cv::namedWindow("topHat", cv::WINDOW_NORMAL);cv::namedWindow("blackHat", cv::WINDOW_NORMAL);cv::Mat img = cv::imread("D:\\personal-data\\wallpapers\\test.png");cv::Mat erosion, dilation, opening, closing, gradient, topHat, blackHat;cv::erode(img, erosion, cv::Mat());cv::dilate(img, dilation, cv::Mat());cv::morphologyEx(img, opening, cv::MORPH_OPEN, cv::Mat());cv::morphologyEx(img, closing, cv::MORPH_CLOSE, cv::Mat());cv::morphologyEx(img, gradient, cv::MORPH_GRADIENT, cv::Mat());cv::morphologyEx(img, topHat, cv::MORPH_TOPHAT, cv::Mat());cv::morphologyEx(img, blackHat, cv::MORPH_BLACKHAT, cv::Mat());cv::imshow("image", img);cv::imshow("erosion", erosion);cv::imshow("dilation", dilation);cv::imshow("opening", opening);cv::imshow("closing", closing);cv::imshow("gradient", gradient);cv::imshow("topHat", topHat);cv::imshow("blackHat", blackHat);cv::waitKey(0);cv::destroyAllWindows();return 0; }

自定義核

在形態學上,核常常稱為“構造元素”,OpenCV提供了創建自定義形態學核的函數cv::getStructuringElement()。

cv::Mat?cv::getStructuringElement(int shape, cv::Size ksize, cv::Point anchor=cv::Point(-1, -1))

cv::getStructuringElement()的元素形狀形狀值元素描述
cv::MOEPH_RECT矩形
cv::MOEPH_ELLIPSE橢圓形以ksize.width和ksize.height為兩個半徑做橢圓
cv::MOEPH_CROSS交叉

,當 i == anchor.y或 j?== anchor.x

用任意線性濾波器做卷積

? ? ? ? ? ? ? ? ? ??

上述兩個核中,左邊的核是可分的,右邊的是不可分的。一個可分核可以理解成兩個一維核,在卷積時先調用x內核,然后再調用y內核。兩個矩陣進行卷積所產生的消耗可以用兩個矩陣的面積之積近似。如此一來,用n×n的核對面積為A的圖像進行卷積所需要的時間是,但如果分解為n×1和1×n的兩個核,那么代價就是。由此可見,分解卷積核可以提高卷積計算的效率。

用cv::filter2D()進行卷積

void cv::filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, cv::Point anchor=cv::Point(-1, -1), double delta=0, int borderType=cv::BORDER_DEFAULT)

注:如果定義了錨點的位置,那么核的大小可以是偶數,否則必須是奇數。

通過cv::sepFilter2D使用可分核

void cv::sepFilter2D(InputArray src, OutputArray dst, int ddepth, InputArray rowKernel,?InputArray? columnKernel,?cv::Point anchor=cv::Point(-1, -1), double delta=0, int borderType=cv::BORDER_DEFAULT)

注:兩個核的大小應當是n1×1和1×n2,n1和n2不一定相等。

生成卷積核

void cv::getDerivKernel(OutputArray kx, OutputArray ky, int dx, int dy, int ksize, bool normalize=true, int ktype=CV_32F)

作用:生成可分解核,如Sobel和Scharr核。dx和dy是求導順序;ksize是核的大小,可以為1、3、5、7或cv::SCHARR;normalize指示是否核元素規范化,如果是浮點型圖像,設為true,反之設為false;ktype表示濾波器的類型,可以使CV_32F和CV_64F。

cv::Mat cv::getGaussianKernel(int ksize, double sigma, int ktype=CV_32F)

作用:生成高斯核。

α在濾波器需要規范化的時候才起作用。sigma可以為-1,這樣將自動計算,其中

總結

以上是生活随笔為你收集整理的OpenCV的滤波与卷积的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。