OpenCV Python教程(3、直方圖的計算與顯示)
本篇文章介紹如何用OpenCV?Python來計算直方圖,并簡略介紹用NumPy和Matplotlib計算和繪制直方圖
直方圖的背景知識、用途什么的就直接略過去了。這里直接介紹方法。
計算并顯示直方圖
與C++中一樣,在Python中調用的OpenCV直方圖計算函數為cv2.calcHist。
cv2.calcHist的原型為:
[python] ?view plaincopy
cv2.calcHist(images,?channels,?mask,?histSize,?ranges[,?hist[,?accumulate?]])? ??
通過一個例子來了解其中的各個參數:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?as?np?? ?? image?=?cv2.imread("D:/histTest.jpg" ,? 0 )?? hist?=?cv2.calcHist([image],?? ????[0 ],? ?? ????None ,? ?? ????[256 ],? ?? ????[0.0 , 255.0 ])? ??
其中第一個參數必須用方括號括起來。
第二個參數是用于計算直方圖的通道,這里使用灰度圖計算直方圖,所以就直接使用第一個通道;
第三個參數是Mask,這里沒有使用,所以用None。
第四個參數是histSize,表示這個直方圖分成多少份(即多少個直方柱)。第二個例子將繪出直方圖,到時候會清楚一點。
第五個參數是表示直方圖中各個像素的值,[0.0, 256.0]表示直方圖能表示像素值從0.0到256的像素。
最后是兩個可選參數,由于直方圖作為函數結果返回了,所以第六個hist就沒有意義了(待確定)
最后一個accumulate是一個布爾值,用來表示直方圖是否疊加。
彩色圖像不同通道的直方圖
彩色圖像不同通道的直方圖
下面來看下彩色圖像的直方圖處理。以最著名的lena.jpg為例,首先讀取并分離各通道:
[python] ?view plaincopy
import ?cv2?????? import ?numpy?as?np?????? ?????? img?=?cv2.imread("D:/lena.jpg" )?????? b,?g,?r?=?cv2.split(img)???
接著計算每個通道的直方圖,這里將其封裝成一個函數:
[python] ?view plaincopy
def ?calcAndDrawHist(image,?color):???? ????hist=?cv2.calcHist([image],?[0 ],? None ,?[ 256 ],?[ 0.0 , 255.0 ])???? ????minVal,?maxVal,?minLoc,?maxLoc?=?cv2.minMaxLoc(hist)???? ????histImg?=?np.zeros([256 , 256 , 3 ],?np.uint8)???? ????hpt?=?int(0.9 *? 256 );???? ???????? ????for ?h? in ?range( 256 ):???? ????????intensity?=?int(hist[h]*hpt/maxVal)???? ????????cv2.line(histImg,(h,256 ),?(h, 256 -intensity),?color)???? ???????????? ????return ?histImg;???
這里只是之前代碼的簡單封裝,所以注釋就省掉了。
接著在主函數中使用:
[python] ?view plaincopy
if ?__name__?==? '__main__' :???? ????img?=?cv2.imread("D:/lena.jpg" )???? ????b,?g,?r?=?cv2.split(img)???? ???? ????histImgB?=?calcAndDrawHist(b,?[255 ,? 0 ,? 0 ])???? ????histImgG?=?calcAndDrawHist(g,?[0 ,? 255 ,? 0 ])???? ????histImgR?=?calcAndDrawHist(r,?[0 ,? 0 ,? 255 ])???? ???????? ????cv2.imshow("histImgB" ,?histImgB)???? ????cv2.imshow("histImgG" ,?histImgG)???? ????cv2.imshow("histImgR" ,?histImgR)???? ????cv2.imshow("Img" ,?img)???? ????cv2.waitKey(0 )???? ????cv2.destroyAllWindows()???
這樣就能得到三個通道的直方圖了,如下:
更進一步
這樣做有點繁瑣,參考abid rahman的做法,無需分離通道,用折線來描繪直方圖的邊界可在一副圖中同時繪制三個通道的直方圖。方法如下:
[python] ?view plaincopy
?? import ?cv2???? import ?numpy?as?np???? ????????? img?=?cv2.imread('D:/lena.jpg' )???? h?=?np.zeros((256 , 256 , 3 ))? ?? ????????? bins?=?np.arange(256 ).reshape( 256 , 1 )? ?? color?=?[?(255 , 0 , 0 ),( 0 , 255 , 0 ),( 0 , 0 , 255 )?]? ?? for ?ch,?col? in ?enumerate(color):???? ????originHist?=?cv2.calcHist([img],[ch],None ,[ 256 ],[ 0 , 256 ])???? ????cv2.normalize(originHist,?originHist,0 , 255 * 0.9 ,cv2.NORM_MINMAX)???? ????hist=np.int32(np.around(originHist))???? ????pts?=?np.column_stack((bins,hist))???? ????cv2.polylines(h,[pts],False ,col)???? ????????? h=np.flipud(h)???? ????????? cv2.imshow('colorhist' ,h)???? cv2.waitKey(0 )????
結果如下圖所示:
代碼說明:
這里的for循環是對三個通道遍歷一次,每次繪制相應通道的直方圖的折線。for循環的第一行是計算對應通道的直方圖,經過上面的介紹,應該很容易就能明白。
這里所不同的是沒有手動的計算直方圖的最大值再乘以一個系數,而是直接調用了OpenCV的歸一化函數。該函數將直方圖的范圍限定在0-255×0.9之間,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個元素四舍六入五湊偶取整(cv2.calcHist函數得到的是float32類型的數組),接著將整數部分轉成np.int32類型。即61.123先轉成61.0,再轉成61。注意,這里必須使用np.int32(...)進行轉換,numpy的轉換函數可以對數組中的每個元素都進行轉換,而Python的int(...)只能轉換一個元素,如果使用int(...),將導致only length-1 arrays can be converted to Python scalars錯誤。
下面的pts = np.column_stack((bins,hist))是將直方圖中每個bin的值轉成相應的坐標。比如hist[0] =3,...,hist[126] = 178,...,hist[255] = 5;而bins的值為[[0],[1],[2]...,[255]]。使用np.column_stack將其組合成[0, 3]、[126, 178]、[255, 5]這樣的坐標作為元素組成的數組。
最后使用cv2.polylines函數根據這些點繪制出折線,第三個False參數指出這個折線不需要閉合。第四個參數指定了折線的顏色。
當所有完成后,別忘了用h = np.flipud(h)反轉繪制好的直方圖,因為繪制時,[0,0]在圖像的左上角。這在直方圖可視化一節中有說明。
NumPy版的直方圖計算
在查閱abid rahman的資料時,發現他用NumPy的直方圖計算函數np.histogram也實現了相同的效果。如下:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?as?np?? ?? img?=?cv2.imread('D:/lena.jpg' )?? h?=?np.zeros((300 , 256 , 3 ))?? bins?=?np.arange(257 )?? bin?=?bins[0 :- 1 ]?? color?=?[?(255 , 0 , 0 ),( 0 , 255 , 0 ),( 0 , 0 , 255 )?]?? ?? for ?ch,col? in ?enumerate(color):?? ????item?=?img[:,:,ch]?? ????N,bins?=?np.histogram(item,bins)?? ????v=N.max()?? ????N?=?np.int32(np.around((N*255 )/v))?? ????N=N.reshape(256 , 1 )?? ????pts?=?np.column_stack((bin,N))?? ????cv2.polylines(h,[pts],False ,col)?? ?? h=np.flipud(h)?? ?? cv2.imshow('img' ,h)?? cv2.waitKey(0 )??
效果圖和上面的一個相同。NumPy的histogram函數將在NumPy通用函數這篇博文中介紹,這里就不詳細解釋了。這里采用的是與一開始相同的比例系數的方法,參考本文的第二節。
另外,通過NumPy和matplotlib可以更方便的繪制出直方圖,下面的代碼供大家參考,如果有機會,再寫的專門介紹matplotlib的文章。
[python] ?view plaincopy
import ?matplotlib.pyplot?as?plt?? import ?numpy?as?np?? import ?cv2?? ?? img?=?cv2.imread('D:/lena.jpg' )?? bins?=?np.arange(257 )?? ?? item?=?img[:,:,1 ]?? hist,bins?=?np.histogram(item,bins)?? width?=?0.7 *(bins[ 1 ]-bins[ 0 ])?? center?=?(bins[:-1 ]+bins[ 1 :])/ 2 ?? plt.bar(center,?hist,?align?=?'center' ,?width?=?width)?? plt.show()??
這里顯示的是綠色通道的直方圖。
OpenCV-Python教程(4、形態學處理)
提示:
轉載請詳細注明原作者及出處,謝謝! 本文介紹使用OpenCV-Python進行形態學處理 本文不介紹形態學處理的基本概念,所以讀者需要預先對其有一定的了解。
定義結構元素
形態學處理的核心就是定義結構元素,在OpenCV-Python中,可以使用其自帶的getStructuringElement函數,也可以直接使用NumPy的ndarray來定義一個結構元素。首先來看用getStructuringElement函數定義一個結構元素:
[python] ?view plaincopy
element?=?cv2.getStructuringElement(cv2.MORPH_CROSS,( 5 , 5 ))??
這就定義了一個5×5的十字形結構元素,如下:
也可以用NumPy來定義結構元素,如下:
[python] ?view plaincopy
NpKernel?=?np.uint8(np.zeros(( 5 , 5 )))?? for ?i? in ?range( 5 ):?? ????NpKernel[2 ,?i]?=? 1 ? ?? ????NpKernel[i,?2 ]?=? 1 ??
這兩者方式定義的結構元素完全一樣:
[python] ?view plaincopy
[[ 0 ? 0 ? 1 ? 0 ? 0 ]?? ?[0 ? 0 ? 1 ? 0 ? 0 ]?? ?[1 ? 1 ? 1 ? 1 ? 1 ]?? ?[0 ? 0 ? 1 ? 0 ? 0 ]?? ?[0 ? 0 ? 1 ? 0 ? 0 ]]??
這里可以看出,用OpenCV-Python內置的常量定義橢圓(MORPH_ELLIPSE)和十字形結構(MORPH_CROSS)元素要簡單一些,如果定義矩形(MORPH_RECT)和自定義結構元素,則兩者差不多。
本篇文章將用參考資料1中的相關章節的圖片做測試:
腐蝕和膨脹
下面先以腐蝕圖像為例子介紹如何使用結構元素:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?as?np?? ??? img?=?cv2.imread('D:/binary.bmp' , 0 )?? ?? kernel?=?cv2.getStructuringElement(cv2.MORPH_RECT,(3 ,? 3 ))?? ?? ?? eroded?=?cv2.erode(img,kernel)?? ?? cv2.imshow("Eroded?Image" ,eroded);?? ?? ?? dilated?=?cv2.dilate(img,kernel)?? ?? cv2.imshow("Dilated?Image" ,dilated);?? ?? cv2.imshow("Origin" ,?img)?? ?? ?? NpKernel?=?np.uint8(np.ones((3 , 3 )))?? Nperoded?=?cv2.erode(img,NpKernel)?? ?? cv2.imshow("Eroded?by?NumPy?kernel" ,Nperoded);?? ?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
如上所示,腐蝕和膨脹的處理很簡單,只需設置好結構元素,然后分別調用cv2.erode(...)和cv2.dilate(...)函數即可,其中第一個參數是需要處理的圖像,第二個是結構元素。返回處理好的圖像。
結果如下:
開運算和閉運算
了解形態學基本處理的同學都知道,開運算和閉運算就是將腐蝕和膨脹按照一定的次序進行處理。但這兩者并不是可逆的,即先開后閉并不能得到原先的圖像。代碼示例如下:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?as?np?? ??? img?=?cv2.imread('D:/binary.bmp' , 0 )?? ?? kernel?=?cv2.getStructuringElement(cv2.MORPH_RECT,(5 ,? 5 ))?? ?? ?? closed?=?cv2.morphologyEx(img,?cv2.MORPH_CLOSE,?kernel)?? ?? cv2.imshow("Close" ,closed);?? ?? ?? opened?=?cv2.morphologyEx(img,?cv2.MORPH_OPEN,?kernel)?? ?? cv2.imshow("Open" ,?opened);?? ?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
閉運算用來連接被誤分為許多小塊的對象,而開運算用于移除由圖像噪音形成的斑點。因此,某些情況下可以連續運用這兩種運算。如對一副二值圖連續使用閉運算和開運算,將獲得圖像中的主要對象。同樣,如果想消除圖像中的噪聲(即圖像中的“小點”),也可以對圖像先用開運算后用閉運算,不過這樣也會消除一些破碎的對象。
對原始圖像進行開運算和閉運算的結果如下:
用形態學運算檢測邊和角點
這里通過一個較復雜的例子介紹如何用形態學算子檢測圖像中的邊緣和拐角(這里只是作為介紹形態學處理例子,實際使用時請用Canny或Harris等算法)。
檢測邊緣
形態學檢測邊緣的原理很簡單,在膨脹時,圖像中的物體會想周圍“擴張”;腐蝕時,圖像中的物體會“收縮”。比較這兩幅圖像,由于其變化的區域只發生在邊緣。所以這時將兩幅圖像相減,得到的就是圖像中物體的邊緣。這里用的依然是參考資料1中相關章節的圖片:
代碼如下:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?? ?? image?=?cv2.imread("D:/building.jpg" , 0 );?? ?? element?=?cv2.getStructuringElement(cv2.MORPH_RECT,(3 ,? 3 ))?? dilate?=?cv2.dilate(image,?element)?? erode?=?cv2.erode(image,?element)?? ?? ?? result?=?cv2.absdiff(dilate,erode);?? ?? ?? retval,?result?=?cv2.threshold(result,?40 ,? 255 ,?cv2.THRESH_BINARY);??? ?? result?=?cv2.bitwise_not(result);??? ?? cv2.imshow("result" ,result);??? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
處理結果如下:
檢測拐角
與邊緣檢測不同,拐角的檢測的過程稍稍有些復雜。但原理相同,所不同的是先用十字形的結構元素膨脹像素,這種情況下只會在邊緣處“擴張”,角點不發生變化。接著用菱形的結構元素腐蝕原圖像,導致只有在拐角處才會“收縮”,而直線邊緣都未發生變化。
第二步是用X形膨脹原圖像,角點膨脹的比邊要多。這樣第二次用方塊腐蝕時,角點恢復原狀,而邊要腐蝕的更多。所以當兩幅圖像相減時,只保留了拐角處。示意圖如下(示意圖來自參考資料1):
代碼如下:
[python] ?view plaincopy
?? import ?cv2?? ?? image?=?cv2.imread("D:/building.jpg" ,? 0 )?? origin?=?cv2.imread("D:/building.jpg" )?? ?? cross?=?cv2.getStructuringElement(cv2.MORPH_CROSS,(5 ,? 5 ))?? ?? diamond?=?cv2.getStructuringElement(cv2.MORPH_RECT,(5 ,? 5 ))?? diamond[0 ,? 0 ]?=? 0 ?? diamond[0 ,? 1 ]?=? 0 ?? diamond[1 ,? 0 ]?=? 0 ?? diamond[4 ,? 4 ]?=? 0 ?? diamond[4 ,? 3 ]?=? 0 ?? diamond[3 ,? 4 ]?=? 0 ?? diamond[4 ,? 0 ]?=? 0 ?? diamond[4 ,? 1 ]?=? 0 ?? diamond[3 ,? 0 ]?=? 0 ?? diamond[0 ,? 3 ]?=? 0 ?? diamond[0 ,? 4 ]?=? 0 ?? diamond[1 ,? 4 ]?=? 0 ?? square?=?cv2.getStructuringElement(cv2.MORPH_RECT,(5 ,? 5 ))?? x?=?cv2.getStructuringElement(cv2.MORPH_CROSS,(5 ,? 5 ))?? ?? result1?=?cv2.dilate(image,cross)?? ?? result1?=?cv2.erode(result1,?diamond)?? ?? ?? result2?=?cv2.dilate(image,?x)?? ?? result2?=?cv2.erode(result2,square)?? ?? ?? ?? result?=?cv2.absdiff(result2,?result1)?? ?? retval,?result?=?cv2.threshold(result,?40 ,? 255 ,?cv2.THRESH_BINARY)?? ?? ?? for ?j? in ?range(result.size):?? ????y?=?j?/?result.shape[0 ]??? ????x?=?j?%?result.shape[0 ]??? ?? ????if ?result[x,?y]?==? 255 :?? ????????cv2.circle(image,?(y,?x),?5 ,?( 255 , 0 , 0 ))?? ?? cv2.imshow("Result" ,?image)?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
注意,由于封裝的緣故,OpenCV中函數參數中使用的坐標系和NumPy的ndarray的坐標系是不同的,在46行可以看出來。
抽空我向OpenCV郵件列表提一下,看我的理解是不是正確的。
大家可以驗證一下,比如在代碼中插入這兩行代碼,就能知道結果了:
[python] ?view plaincopy
cv2.circle(image,?( 5 ,? 10 ),? 5 ,?( 255 , 0 , 0 ))?? image[5 ,? 10 ]?=? 0 ??
通過上面的代碼就能檢測到圖像中的拐角并標出來,效果圖如下:
當然,這只是個形態學處理示例,檢測結果并不好。
未完待續...
在將來的某一篇文章中將做個總結,介紹下OpenCV中常用的函數,如threshold、bitwise_xxx,以及繪制函數等。
參考資料:
1、《Opencv2 Computer Vision Application Programming Cookbook》
2、《OpenCV References Manule》
如果覺得本文寫的還可以的話,請輕點“頂”,方便讀者、以及您的支持是我寫下去的最大的兩個動力。
OpenCV-Python教程(5、初級濾波內容)
本篇文章介紹如何用OpenCV-Python來實現初級濾波功能。
提示:
轉載請詳細注明原作者及出處,謝謝! 本文介紹使用OpenCV-Python實現基本的濾波處理 本文不介紹濾波處理的詳細概念,所以讀者需要預先對其有一定的了解。
簡介
過濾是信號和圖像處理中基本的任務。其目的是根據應用環境的不同,選擇性的提取圖像中某些認為是重要的信息。過濾可以移除圖像中的噪音、提取感興趣的可視特征、允許圖像重采樣,等等。其源自于一般的信號和系統理論,這里將不介紹該理論的細節。但本章會介紹關于過濾的基本概念,以及如何在圖像處理程序中使用濾波器。首先,簡要介紹下頻率域分析的概念。
當我們觀察一張圖片時,我們觀察的是圖像中有多少灰度級(或顏色)及其分布。根據灰度分布的不同來區分不同的圖像。但還有其他方面可以對圖像進行分析。我們可以觀察圖像中灰度的變化。某些圖像中包含大量的強度不變的區域(如藍天),而在其他圖像中的灰度變化可能會非常快(如包含許多小物體的擁擠的圖像)。因此,觀察圖像中這些變化的頻率就構成了另一條分類圖像的方法。這個觀點稱為頻域。而通過觀察圖像灰度分布來分類圖像稱為空間域。
頻域分析將圖像分成從低頻到高頻的不同部分。低頻對應圖像強度變化小的區域,而高頻是圖像強度變化非常大的區域。目前已存在若干轉換方法,如傅立葉變換或余弦變換,可以用來清晰的顯示圖像的頻率內容。注意,由于圖像是一個二維實體,所以其由水平頻率(水平方向的變化)和豎直頻率(豎直方向的變化)共同組成。
在頻率分析領域的框架中,濾波器是一個用來增強圖像中某個波段或頻率并阻塞(或降低)其他頻率波段的操作。低通濾波器是消除圖像中高頻部分,但保留低頻部分。高通濾波器消除低頻部分
本篇文章介紹在OpenCV-Python中實現的初級的濾波操作,下一篇文章介紹更加復雜的濾波原理及其實現。
本篇文章使用傳統的lena作為實驗圖像。
用低通濾波來平滑圖像
低通濾波器的目標是降低圖像的變化率。如將每個像素替換為該像素周圍像素的均值。這樣就可以平滑并替代那些強度變化明顯的區域。在OpenCV中,可以通過blur函數做到這一點:
[python] ?view plaincopy
dst?=?cv2.blur(image,( 5 , 5 ));???
其中dst是blur處理后返回的圖像,參數一是輸入的待處理圖像,參數2是低通濾波器的大小。其后含有幾個可選參數,用來設置濾波器的細節,具體可查閱參考資料2。不過這里,這樣就夠了。下面是一個簡單的示例代碼:
[python] ?view plaincopy
?? import ?cv2?? ?? img?=?cv2.imread("D:/lena.jpg" ,? 0 )?? result?=?cv2.blur(img,?(5 , 5 ))?? ?? cv2.imshow("Origin" ,?img)?? cv2.imshow("Blur" ,?result)?? ?? cv2.waitKey(0 )?? cv2.destroyAllWindows()??
結果如下,左邊是平滑過的圖像,右邊是原圖像:
這種濾波器又稱為boxfilter(注,這與化學上的箱式過濾器是兩碼事,所以這里就不翻譯了)。所以也可通過OpenCV的cv2.bofxfilter(...)函數來完成相同的工作。如下:
[python] ?view plaincopy
result1?=?cv2.boxFilter(img,?- 1 ,?( 5 ,? 5 ))??
這行代碼與上面使用blur函數的效果完全相同。其中第二個參數的-1表示輸出圖像使用的深度與輸入圖像相同。后面還有幾個可選參數,具體可查閱OpenCV文檔。
高斯模糊
在某些情況下,需要對一個像素的周圍的像素給予更多的重視。因此,可通過分配權重來重新計算這些周圍點的值。這可通過高斯函數(鐘形函數,即喇叭形數)的權重方案來解決。cv::GaussianBlur函數可作為濾波器用下面的方法調用:
[python] ?view plaincopy
gaussianResult?=?cv2.GaussianBlur(img,( 5 , 5 ), 1.5 )??
區別
低通濾波與高斯濾波的不同之處在于:低通濾波中,濾波器中每個像素的權重是相同的,即濾波器是線性的。而高斯濾波器中像素的權重與其距中心像素的距離成比例。關于高斯模糊的詳細內容,抽空將寫一篇獨立的文章介紹。
使用中值濾波消除噪點
前面介紹的是線性過濾器,這里介紹非線性過濾器——中值濾波器。由于中值濾波器對消除椒鹽現象特別有用。所以我們使用第二篇教程中椒鹽函數先對圖像進行處理,將處理結果作為示例圖片。
調用中值濾波器的方法與調用其他濾波器的方法類似,如下:
[python] ?view plaincopy
result?=?cv2.medianBlur(image, 5 )??
函數返回處理結果,第一個參數是待處理圖像,第二個參數是孔徑的尺寸,一個大于1的奇數。比如這里是5,中值濾波器就會使用5×5的范圍來計算。即對像素的中心值及其5×5鄰域組成了一個數值集,對其進行處理計算,當前像素被其中值替換掉。
如果在某個像素周圍有白色或黑色的像素,這些白色或黑色的像素不會選擇作為中值(最大或最小值不用),而是被替換為鄰域值。代碼如下:
[python] ?view plaincopy
?? import ?cv2?? import ?numpy?as?np???? ???? def ?salt(img,?n):???? ????for ?k? in ?range(n):???? ????????i?=?int(np.random.random()?*?img.shape[1 ]);???? ????????j?=?int(np.random.random()?*?img.shape[0 ]);???? ????????if ?img.ndim?==? 2 :????? ????????????img[j,i]?=?255 ???? ????????elif ?img.ndim?==? 3 :????? ????????????img[j,i,0 ]=? 255 ???? ????????????img[j,i,1 ]=? 255 ???? ????????????img[j,i,2 ]=? 255 ???? ????return ?img??? ?? img?=?cv2.imread("D:/lena.jpg" ,? 0 )?? result?=?salt(img,?500 )?? median?=?cv2.medianBlur(result,?5 )?? ?? ?? cv2.imshow("Salt" ,?result)?? cv2.imshow("Median" ,?median)?? ?? cv2.waitKey(0 )??
處理結果如下:
由于中值濾波不會處理最大和最小值,所以就不會受到噪聲的影響。相反,如果直接采用blur進行均值濾波,則不會區分這些噪聲點,濾波后的圖像會受到噪聲的影響。
中值濾波器在處理邊緣也有優勢。但中值濾波器會清除掉某些區域的紋理(如背景中的樹)。
其他
由于方向濾波器與這里的原理有較大的出入,所以將用獨立的一篇文章中介紹其原理以及實現。
參考資料:
1、《Opencv2 Computer Vision Application Programming Cookbook》
2、《OpenCV References Manule》
from: http://blog.csdn.net/sunny2038/article/category/904451
總結
以上是生活随笔 為你收集整理的OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。