基于C++的OpenCV(八)图像处理
一、線性濾波:方框濾波、均值濾波、高斯濾波
1.1.平滑處理
平滑處理(smoothing)也稱為模糊處理(bluring),是一種簡單且使用頻率很高的圖像處理方法。平滑處理的用途有很多,最常見的是用來減少圖像上的噪點或者失真。
1.2.圖像濾波與濾波器
圖像濾波指在盡量保留圖像細節特征的條件下對目標圖像的噪聲進行抑制,是圖像預處理中不可缺少的操作,其處理效果的好壞直接影響到后續圖像處理和分析的有效性和可靠性。
消除圖像中的噪聲成分叫作圖像的平滑化或濾波操作。信號或圖像的能量大部分集中在幅度譜的低頻和中頻段,而在高頻段,有用的信息經常被噪聲淹沒。
圖像濾波的目的有兩個:
 一個是抽出對象的特征作為圖像識別的特征模式;
 另一個是為適應圖像處理的要求,消除圖像數字化時所混入的噪聲。
濾波處理的要求:
 一是不能損壞圖像的輪廓及邊緣等重要信息;
 二是使圖像清晰視覺效果好。
平滑濾波是低頻增強的空間域濾波技術。它的目的有兩類:一類是模糊;另一類是消除噪聲。
空間域的平滑濾波一般采用簡單平均法進行,就是求鄰近像元點的平均亮度值。鄰域的大小與平滑效果直接相關,鄰域越大平滑的效果越好,但鄰域過大,平滑也會使邊緣信息損失的越大,從而使輸出的圖像變得模糊,因此需合理選擇鄰域的大小。
方框濾波——BoxBlur函數
 均值濾波(鄰域平均濾波)——Blur函數
 高斯濾波——GaussianBlur函數
 中值濾波——medianBlur函數
 雙邊濾波——bilateralFilter函數
1.3.線性濾波器的簡介
線性濾波器:線性濾波器經常用于剔除輸入信號中不想要的頻率或者從許多頻率中選擇一個想要的頻率。
幾種常見的線性濾波器:
 低通濾波器:允許低頻率通過;
 高通濾波器:允許高頻率通過;
 帶通濾波器:允許一定范圍頻率通過;
 帶阻濾波器:阻止一定范圍頻率通過并且允許其他頻率通過;
 全通濾波器:允許所有頻率通過,僅僅改變相位關系;
 陷波濾波器(Band-Stop Filter):阻止一個狹小頻率范圍通過,是一種特殊帶阻濾波器。
1.4.濾波和模糊
濾波可分為低通濾波和高通濾波兩種:高通濾波是指用高斯函數作為濾波函數的濾波操作,至于是不是模糊,要看是高斯低通還是高斯高通,低通就是模糊,高通就是銳化。
高斯濾波是指用高斯函數作為濾波函數的濾波操作。
 高斯模糊就是高斯低通濾波。
1.5.領域算子與線性領域濾波
鄰域算子(局部算子)是利用給定像素周圍的像素值的決定此像素的最終輸出值的一種算子。而線性領域濾波就是一種常見的領域算子,像素的輸出值取決于輸入像素的加權和。
鄰域算子除了用于局部色調調整以外,還可以用于圖像濾波,以實現圖像的平滑和銳化,圖像邊緣增強或者圖像噪聲的去除。
線性濾波處理的輸出像素值是輸入像素值的加權和。
 方框濾波——boxblur函數
 均值濾波——blur函數
 高斯濾波——GaussianBlur函數
1.6.方框濾波(box Filter)
方框濾波(box Filter)被封裝在一個名為boxblur的函數中,即boxblur函數的作用是使用方框濾波器(box filter)來模糊一張圖片,從src輸入,從dst輸出。
void boxFilter(InputArray src,OutputArray dsy,int depth,Size ksize,Point anchor=Point(-1,-1),boolnormalize=true,int borderType=BORDER_DEFAULT)第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片。但需要注意,待處理的圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。
 第三個參數,int類型的ddepth,輸出圖像的深度,-1代表使用原圖深度,即src.depth()。
 第四個參數,Size類型(對Size類型稍后有講解)的ksize,內核的大小。一般用Size(w,h)來表示內核的大小,其中w為像素寬度,h為像素高度。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5X5的核的中心。
 第六個參數,bool類型的normalize,默認值為true,一個標識符,表示內核是否被其區域歸一化(normalized)了。
 第七個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
1.7.均值濾波
均值濾波是最簡單的一種濾波操作,輸出圖像的每一個像素是核窗口內輸入圖像對應像素的平均值(所有像素加權系數相等),其實就是歸一化后的方框濾波。
1.均值濾波基礎理論
 均值濾波是典型的線性濾波算法,主要方法為領域平均法,即用一片圖像區域的各個像素的平均值來代替原圖像中的各個像素值。一般需要在圖像上對目標像素給出一個模板(內核),該模板包括了其周圍的臨近像素(比如以目標像素為中心的周圍8(3*3-1)個像素,構成一個濾波模板,即去掉目標像素本身)。再用模板中的全體像素的平均值來代替原來像素值。
2.均值濾波的缺陷
 均值濾波本身存在著固有缺陷,即它不能很好的保護圖像細節,在圖像去噪的同時也破壞了圖像的細節部分,從而使圖像變得模糊,不能很好地去除噪聲點。
3.在opencv中使用均值濾波——blur函數
 blur函數的作用是:對輸入的圖像src進行均值濾波后用dst輸出。
第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片。但需要注意的是,待處理的圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。可以用Mat::Clone,以源圖片為模板,來初始化得到目標圖。
 第三個參數,Size類型的ksize,內核的大小。一般寫作Size(w,h)來表示內核的大小。
 第四個參數,Point類型的anchor,表示錨點(即被平滑的那個點),它的默認值Point(-1,-1)。如果這個點坐標是負值,就表示取核的中心為錨點,所以默認值Point(-1,-1)表示這個錨點在核的中心。
 第五個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
1.8.高斯濾波
1)高斯濾波基礎理論
 高斯濾波是一種線性平滑濾波,可以消除高斯噪聲,廣泛應用于圖像處理的減噪過程。通俗地講,高斯濾波就是對整幅圖像進行加權平均地過程,每一個像素點地值,都是其本身和領域內的其他像素值經過加權平均后得到。
高斯濾波的具體操作是:用一個模板(或稱卷積、掩膜)掃描圖像中的每一個像素,用模板確定的鄰域內像素的加權平均灰度值去替代模板中心像素點的值。
圖像與圓形方框模糊做卷積將會生成更加精確的焦外成像效果。由于高斯函數的傅里葉變換是另外一個高斯函數,所以高斯模糊對于圖像來說就是一個低通濾波操作。
 高斯濾波器是一類根據高斯函數的形狀來選擇權值的線性平滑濾波器。高斯平滑濾波器對于抑制服從正態分布的噪聲非常有效。
2)高斯濾波:GaussianBlur函數
 GaussianBlur函數的作用是用高斯濾波器來模糊一張圖片,對輸入的圖像src進行高斯濾波后用dst輸出。它將原圖像和指定的高斯核函數做卷積運算,并且支持就地過濾(In-placefiltering)。
 void GaussianBlur(InputArray src,OutputArray dst,Size ksize,double sigmaX,double sigmaY=0,int borderType=BORDER_DEFAULT)
 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象。可以是單獨的任意通道數的圖片,其圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。可以用Mat::Clone,以源圖片為模板,來初始化得到目標圖。
 第三個參數,Size類型的ksize高斯內核的大小。其中ksize.width和ksize.height可以不同,但他們都必須為正數和奇數,或者是零,這都由sigma計算而來。
 第四個參數,double類型的sigmaX,表示高斯核函數在X方向的標準偏差。
 第五個參數,double類型的sigmaY,表示高斯核函數在Y方向的標準偏差。若sigmaY為零,就將他設為sigmaX;如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height計算出來。
 第六個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
1.9.線性濾波相關OpenCV源碼
【1】OpenCV中boxFilter函數源碼
void cv::boxFilter(InputArray _src,OutputArray _dst,int ddepth,Size ksize,Point anchor,bool ksize,Point anchor,bool normalize,int borderType) {Mat src=_src.getMat();int sdepth=src.depth(),cn=src.channels();if(ddepth<0)ddepth=sdepth;_dst.create(src.size(),CV_MAKETYPE(ddepth,cn));Mat dst=_dst.getMat();if(borderType!=BORDER_CONSTANT&&normalize){if(src.rows==1)ksize.height=1;if(src.cols==1)ksize.width=1;}#ifdef HAVE_TEGRA_OPTIMIZATIONif(tegra::box(src,dst,ksize,anchor,normalize,borderType))return;#endifPtr<FilterEngine> f=createBoxFilter(src.type(),dst.type(),ksize,anchor,normalize,borderType);f->apply(src,dst); }其中,Ptr是用來動態分配的對象的智能指針模板類。函數內部先復制源圖的形參Mat數據到臨時變量,定義一些臨時變量,再處理ddepth小于零的情況,接著處理borderType不為BORDER_CONSTANT且normalize為真的情況,最終調用FilterEngine濾波引擎創建一個BoxFilter,正式開始濾波操作。
【2】FilterEngine類解析:OpenCV圖像濾波核心引擎
 FilterEngine類是OpenCV關于圖像濾波的主要類,是OpenCV圖像濾波功能額核心引擎。各種濾波函數如blur、GaussianBlur其實是就是再函數末尾處定義了一個Ptr類型的f,然后f->apply(src,dst)。
PtrCreateLinearFilter(int srcType,int dstType,InputArray kernel,Point_anchor=Point(-1,-1),double delta=0,int rowBorderType=BORDER_DEFAULT,intcolumnBorderType=-1,const Scalar&borderValue=Scalar())
 其中的Ptr是用來動態分配的對象的智能指針模板類,而尖括號里的模板參數就是FilterEngine。
使用FilterEngine類可以分塊處理大量的圖像,構建復雜的管線,其中就包含一些進行濾波階段。如果我們需要使用預定義好的濾波操作,有cv::filter2D()、cv::erode()和cv::dilate()可以選擇,他們不依賴于FilterEngine,在自己函數體內部實現了FilterEngine提供的功能。
class CV_EXPORTS FilterEngine {public://默認構造函數FilterEngine();//完整的構造函數FilterEngine(const Ptr<BaseFilter>& _filter2D,const Ptr_<BaseRowFilter>& _rowFilter,const Ptr<BaseColumnFilter>& _columnFilter,int srcType,int dstType,int bufType,int _rowBorderType=BORDER_REPLICATE,int _columnBorderType=-1,const Scalar&_borderValue=Scalar());//默認析構函數virtual ~FilterEngine();//重新初始化引擎。釋放之前濾波器申請的內存void init(const Ptr<BaseFilter>& _filter2D,constPtr<BaseRowFilter>& _rowFilter,constPtr<BaseColumnFilter>& _columnFilter,int srcType,int dstType,int bufType,int _rowBorderType=BORDER_REPLICALE,int _columnBorderType=-1,const Scalar&_borderValue=Scalar());//開始對指定了ROI區域和尺寸的圖片進行濾波操作virtual int start(Size wholeSize,Rect roi,int maxBufRows=-1);//開始對制定了ROI區域的圖片進行濾波操作virtual int start(const Mat& src,const Rect&srcRoi=Rect(0,0,-1,-1),bool isolated=false,intmaxBufRows=-1);//處理圖像的下一個srcCount行virtual int proceed(const uchar*src,int srcStep,int srcCount,uchar*dst,intdstStep);//對圖像指定的ROI區域進行濾波操作,若srcRoi=(0,0,-1,-1),則對整個圖像進行濾波操作virtual void applay(const Mat&src,Mat& dst,const Rect&srcRoi=Rect(0,0,-1,-1),Point dstOfs=Point(0,0),bool isolated=false);//如果濾波器可分離,則返回trueboolisSeparable() const{return (const BaseFilter*)filter2D==0;}//返回輸入和輸出行數int remainingInputRows() const;intremainingOutputRows() const;//一些成員參數定義int srcType,dstType,bufType;Size ksize;Point anchor;int maxWidth;Size wholeSize;Rect roi;int dx1,dx2;int rowBorderType,columnBorderType;vector<int>borderTab;int borderElemSize;vector<uchar>ringBuf;vector<uchar>srcRow;vector<uchar>constBorderValue;vector<uchar>constBorderRow;int bufStep,startY,startY0,endY,rowCount,dstY;vector<uchar*>rows;Ptr<BaseFilter>filter2D;Ptr<BaseRowFilter>rowFilter;Ptr<BaseColumnFilter>ColumnFilter; };【3】OpenCV中blur函數源碼
void cv::blur(InputArray src,OutputArray dst,Size ksize,Point anchor,int borderType){boxFilter(src,dst,-1,ksize,anchor,true,borderType);}1.10.OpenCV中GaussianBlur函數源碼
void cv::GaussianBlur(InputArray _src,OutputArray _dst,Size ksize,double siama1,double sigma2,int borderType) {//復制形參Mat數據到臨時變量Mat src=_src.getMat();_dst.create(src.size(),src.type());Mat dst=_dst.getMat();//處理邊界選項不為BORDER_CONSTANT時的情況if(borderType!=BORDER_CONSTANT){if(src.rows==1)ksize.height=1;if(src.cols==1)ksize.width=1;}//若ksize長寬都為1,將源圖復制給目標圖if(ksize.width==1&&ksize.height==1){src.copyTo(dst);return;}#ifdef HAVE_TEGRA_OPTIMIZATIONif(sigma1==0&&sigma2==0&&tegra::gaussian(src,dst,ksize,borderType)return;#endif#if defined HAVE_IPP&&(IPP_VERSION_MOJOR>=7)if(src.type()==CV_32F1&&sigma1==sigma2&&ksize.width==ksize.height&&sigma1!=0.0){IppiSize roi=(src.cols,src.rows);int bufSize=0;ippiFilterGaussGetBufferSize_32f_CLR(roi,ksize.width,&bufSize0;AutoBuffer<uchar>buf(bufSize+128);if(ippiFilterGaussBorder_32f_CLR((const Ipp32f*)src.data,(int)src.step,(Ipp32f*)dst.data,(int)dst.step,roi,ksize.width,(Ipp32f)sigma1,(IppoBorderType)borderType,0.0,alignPtr(&buf[0],32))>=0)return;}#endifPtr<FilterEngine> f=createGaussianFilter(src.type(),ksize,sigma1,sigma2,borderType);f->apply(src,dst);}1.11.線性濾波核心API函數
【1】方框濾波:boxFilter函數
 boxFilter的函數作用是使用方框濾波(box filter)來模糊一張圖片,由src輸入,dst輸出。
第一個參數:InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象。該函數對通道是獨立處理的,且可以處理任意通道數的圖片。但需要注意,待處理的圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數:OutputArray類型的dst,即目標圖像,需要和源圖像有一樣的尺寸和類型。
 第三個參數:int類型的ddepth,輸出圖像的深度。“-1”代表使用原圖深度,即src.depth()。
 第四個參數:Size類型的ksize,內核的大小,一般用Size(w,h)的寫法來表示內核的大小(其中,w為像素寬度,h為像素高度)。
 第五個參數:Point類型的anchor,表示錨點(即被平滑的那個點),注意它有默認值Point(-1,-1)。如果這個點坐標是負值的話,就表示取核的中心為錨點,所以默認值Point(-1,-1)表示這個錨點在核的中心。
 第六個參數:bool類型的normalize,默認值為true,一個標識符,表示內核是否被其區域歸一化(normalized)了。
 第七個參數:int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
【2】均值濾波:blur函數
 blur的作用是對輸入的圖像src進行均值濾波后用dst輸出。
第一個參數:InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象。該函數對通道是獨立處理的,且可以處理任意通道數的圖片。但需要注意,待處理的圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數:OutputArray類型的dst,即目標圖像,需要和源圖像有一樣的尺寸和類型。
 第三個參數:Size類型的ksize,內核的大小,一般用Size(w,h)的寫法來表示內核的大小(其中,w為像素寬度,h為像素高度)。
 第四個參數:Point類型的anchor,表示錨點(即被平滑的那個點),注意它有默認值Point(-1,-1)。如果這個點坐標是負值的話,就表示取核的中心為錨點,所以默認值Point(-1,-1)表示這個錨點在核的中心。
 第五個參數:int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
【3】高斯濾波:GaussianBlur函數
 GaussianBlur函數的作用是用高斯濾波器來模糊一張圖片,對輸入的圖像src進行高斯濾波后用dst輸出。
第一個參數:InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象。該函數對通道是獨立處理的,且可以處理任意通道數的圖片。但需要注意,待處理的圖片深度應該為CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F之一。
 第二個參數:OutputArray類型的dst,即目標圖像,需要和源圖像有一樣的尺寸和類型。
 第三個參數:Size類型的ksize,內核的大小,一般用Size(w,h)的寫法來表示內核的大小(其中,w為像素寬度,h為像素高度)。
 第四個參數:double類型的sigmaX,表示高斯核函數在X方向的標準偏差。
 第五個參數:double類型的sigmaY,表示高斯核函數在Y方向的標準偏差。若sigmaY為零,就將它設為sigmaX,如果sigmaX和sigmaY都是0。
 第六個參數:int類型的borderType,用于推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT。
1.12.圖像線性濾波綜合示例
二、非線性濾波:中值濾波、雙邊濾波
2.1.非線性濾波
在很多情況下,使用鄰域像素的非線性濾波會得到更好的效果。比如在噪聲是散粒噪聲而不是高斯噪聲,即圖像偶爾會出現很大的值的時候,用高斯濾波器對圖像進行模糊的話,噪聲像素是不會被去除的,他們只是轉換為更為柔和但仍然可見的散粒。
2.2.中值濾波
中值濾波(Median filter)是一種典型的非線性濾波技術,基本思想是用像素點領域灰度值的中值來代替該像素點的灰度值,該方法在去除脈沖噪聲、椒鹽噪聲的同時又能保留圖像的邊緣細節。
中值濾波是基于排序統計理論的一種能有效抑制噪聲的非線性信號處理技術,其基本原理是把數字圖像或數字序列中一點的值用該點的一個鄰域中各點值得中指代替,讓周圍得像素值接近真實值,從而消除孤立得噪聲點。這對于斑點噪聲(speckle noise)和椒鹽噪聲(salt-and-pepper noise)來說尤其有用,因為它不依賴于鄰域內那些與典型值差別很大得值。中值濾波器在處理連續圖像窗函數時與線性濾波器得工作方式類似,但濾波過程卻不再是加權運算。
中值濾波在一定的條件下可以克服常見線性濾波器,如最小均方濾波、方框濾波器、均值濾波等帶來的圖像細節模糊,而且對濾除脈沖干擾及圖像掃描噪聲非常有效,也常用于保護邊緣信息。保存邊緣的特性使它在不希望出現邊緣模糊的場合也很有用,是非常經典的平滑噪聲處理方法。
中值濾波與均值濾波器比較
 優勢:在均值濾波器中,由于噪聲成分被放入平均計算中,所以輸出受到了噪聲的影響。但在中值濾波器中,由于噪聲成分很難選上,所以幾乎不會影響到輸出。
 劣勢:中值濾波花費的時間是均值濾波的5倍以上。
中值濾波選擇每個像素的鄰域像素中的中值作為輸出,或者說中值濾波將每一像素點的灰度值設置為該點某領域窗口內的所有像素點灰度值的中值。
具體操作:
 1)按強度值大小排列像素值;
 2)選擇排序像素集的中間值作為點的新值。
 一般采用奇數點的鄰域來計算中值,但像素點數為偶數時,中值就取排序像素中間兩點的平均值。
中值濾波在一定條件下,可以客服線性濾波器(如均值濾波等)所帶來的圖像細節模糊,對濾除脈沖干擾即像素掃描噪聲最為有效,而且在實際運算過程中并不需要圖像的統計特性,也給計算帶來不少方便。但是對一些細節(特別是細、尖頂等)多的圖像不太適合。
2.3.雙邊濾波
雙邊濾波(Bilateral filter)是一種非線性的濾波方法,是結合圖像的空間鄰近度和像素值相似度的一種折中處理,同時考慮空域信息和灰度相似性,達到保邊去噪的目的,具有簡單、非迭代、局部的特點。
2.4.非線性濾波相關核心API函數
【1】中值濾波:medianBlur函數
【2】雙邊濾波:bilateralFilter函數
2.5.OpenCV中5種圖像濾波綜合
三、形態學濾波(1):腐蝕與膨脹
3.1.形態學概述
形態學(morphology)一詞通常表示生物學的一個分支,該分支主要研究動植物的形態和結構。而我們圖像粗粒中的形態學,往往指的是數學形態學。
數學形態學(Mathematical morphology)是一門建立在格論和拓撲學基礎之上的圖像分析學科,是數學形態學圖像處理的基本理論。其基本的運算包括:二值腐蝕和膨脹、二值開閉運算、骨架抽取、極限腐蝕、擊中擊不中變換、形態學梯度、Top-hat變換、顆粒分析、流域變換、灰值腐蝕和膨脹、灰值開閉運算、灰值形態學梯度等。
3.2.膨脹
3.3.腐蝕
3.4.相關OpenCV源碼
3.5.相關核心API函數
3.6.綜合示例:腐蝕與膨脹
四、形態學濾波(2):開運算、閉運算、形態學梯度、頂帽、黑帽
4.1.開運算
4.2.閉運算
4.3.形態學梯度
4.4.頂帽
4.5.黑帽
4.6.形態學濾波OpenCV源碼
4.7.核心API函數:morphologhEx()
4.8.形態學操作使用
4.9.綜合使用形態學濾波
五、漫水填充
5.1.漫水填充的定義
5.2.漫水填充的基本思想
5.3.實現漫水填充算法:floodFill函數
5.4.漫水填充示例
六、圖像金字塔與圖像尺寸縮放
6.1.概論
6.2.圖像金字塔
6.3.高斯金字塔
6.4.拉普拉斯金字塔
6.5.尺寸調整:resize()函數
6.6.圖像金字塔相關的API函數
6.7.圖像金字塔與圖片尺寸縮放示例
七、閾值化
7.1.固定閾值操作:Threshold()函數
7.2.自適應閾值操作:adaptiveThreshold()函數
7.3.基本閾值操作示例
總結
以上是生活随笔為你收集整理的基于C++的OpenCV(八)图像处理的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: dvajs项目要部署到服务器上,dvaJ
- 下一篇: 导出excel,前后台代码示例
