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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内

發(fā)布時(shí)間:2025/3/21 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

OpenCV Python教程(3、直方圖的計(jì)算與顯示)

本篇文章介紹如何用OpenCV?Python來(lái)計(jì)算直方圖,并簡(jiǎn)略介紹用NumPy和Matplotlib計(jì)算和繪制直方圖

直方圖的背景知識(shí)、用途什么的就直接略過(guò)去了。這里直接介紹方法。

計(jì)算并顯示直方圖

與C++中一樣,在Python中調(diào)用的OpenCV直方圖計(jì)算函數(shù)為cv2.calcHist。

cv2.calcHist的原型為:

[python]?view plaincopy
  • cv2.calcHist(images,?channels,?mask,?histSize,?ranges[,?hist[,?accumulate?]])?#返回hist??
  • 通過(guò)一個(gè)例子來(lái)了解其中的各個(gè)參數(shù): [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np??
  • ??
  • image?=?cv2.imread("D:/histTest.jpg",?0)??
  • hist?=?cv2.calcHist([image],??
  • ????[0],?#使用的通道??
  • ????None,?#沒(méi)有使用mask??
  • ????[256],?#HistSize??
  • ????[0.0,255.0])?#直方圖柱的范圍??
  • 其中第一個(gè)參數(shù)必須用方括號(hào)括起來(lái)。

    第二個(gè)參數(shù)是用于計(jì)算直方圖的通道,這里使用灰度圖計(jì)算直方圖,所以就直接使用第一個(gè)通道;

    第三個(gè)參數(shù)是Mask,這里沒(méi)有使用,所以用None。

    第四個(gè)參數(shù)是histSize,表示這個(gè)直方圖分成多少份(即多少個(gè)直方柱)。第二個(gè)例子將繪出直方圖,到時(shí)候會(huì)清楚一點(diǎn)。

    第五個(gè)參數(shù)是表示直方圖中各個(gè)像素的值,[0.0, 256.0]表示直方圖能表示像素值從0.0到256的像素。

    最后是兩個(gè)可選參數(shù),由于直方圖作為函數(shù)結(jié)果返回了,所以第六個(gè)hist就沒(méi)有意義了(待確定)

    最后一個(gè)accumulate是一個(gè)布爾值,用來(lái)表示直方圖是否疊加。

    彩色圖像不同通道的直方圖

    彩色圖像不同通道的直方圖

    下面來(lái)看下彩色圖像的直方圖處理。以最著名的lena.jpg為例,首先讀取并分離各通道:

    [python]?view plaincopy
  • import?cv2??????
  • import?numpy?as?np??????
  • ??????
  • img?=?cv2.imread("D:/lena.jpg")??????
  • b,?g,?r?=?cv2.split(img)???
  • 接著計(jì)算每個(gè)通道的直方圖,這里將其封裝成一個(gè)函數(shù): [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;???
  • 這里只是之前代碼的簡(jiǎn)單封裝,所以注釋就省掉了。

    接著在主函數(shù)中使用:

    [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()???
  • 這樣就能得到三個(gè)通道的直方圖了,如下:

    更進(jìn)一步

    這樣做有點(diǎn)繁瑣,參考abid rahman的做法,無(wú)需分離通道,用折線來(lái)描繪直方圖的邊界可在一副圖中同時(shí)繪制三個(gè)通道的直方圖。方法如下:

    [python]?view plaincopy
  • #coding=utf-8????
  • import?cv2????
  • import?numpy?as?np????
  • ?????????
  • img?=?cv2.imread('D:/lena.jpg')????
  • h?=?np.zeros((256,256,3))?#創(chuàng)建用于繪制直方圖的全0圖像????
  • ?????????
  • bins?=?np.arange(256).reshape(256,1)?#直方圖中各bin的頂點(diǎn)位置????
  • color?=?[?(255,0,0),(0,255,0),(0,0,255)?]?#BGR三種顏色????
  • 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)????
  • 結(jié)果如下圖所示:


    代碼說(shuō)明:

    這里的for循環(huán)是對(duì)三個(gè)通道遍歷一次,每次繪制相應(yīng)通道的直方圖的折線。for循環(huán)的第一行是計(jì)算對(duì)應(yīng)通道的直方圖,經(jīng)過(guò)上面的介紹,應(yīng)該很容易就能明白。

    這里所不同的是沒(méi)有手動(dòng)的計(jì)算直方圖的最大值再乘以一個(gè)系數(shù),而是直接調(diào)用了OpenCV的歸一化函數(shù)。該函數(shù)將直方圖的范圍限定在0-255×0.9之間,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個(gè)元素四舍六入五湊偶取整(cv2.calcHist函數(shù)得到的是float32類型的數(shù)組),接著將整數(shù)部分轉(zhuǎn)成np.int32類型。即61.123先轉(zhuǎn)成61.0,再轉(zhuǎn)成61。注意,這里必須使用np.int32(...)進(jìn)行轉(zhuǎn)換,numpy的轉(zhuǎn)換函數(shù)可以對(duì)數(shù)組中的每個(gè)元素都進(jìn)行轉(zhuǎn)換,而Python的int(...)只能轉(zhuǎn)換一個(gè)元素,如果使用int(...),將導(dǎo)致only length-1 arrays can be converted to Python scalars錯(cuò)誤。

    下面的pts = np.column_stack((bins,hist))是將直方圖中每個(gè)bin的值轉(zhuǎn)成相應(yīng)的坐標(biāo)。比如hist[0] =3,...,hist[126] = 178,...,hist[255] = 5;而bins的值為[[0],[1],[2]...,[255]]。使用np.column_stack將其組合成[0, 3]、[126, 178]、[255, 5]這樣的坐標(biāo)作為元素組成的數(shù)組。

    最后使用cv2.polylines函數(shù)根據(jù)這些點(diǎn)繪制出折線,第三個(gè)False參數(shù)指出這個(gè)折線不需要閉合。第四個(gè)參數(shù)指定了折線的顏色。

    當(dāng)所有完成后,別忘了用h = np.flipud(h)反轉(zhuǎn)繪制好的直方圖,因?yàn)槔L制時(shí),[0,0]在圖像的左上角。這在直方圖可視化一節(jié)中有說(shuō)明。

    NumPy版的直方圖計(jì)算

    在查閱abid rahman的資料時(shí),發(fā)現(xiàn)他用NumPy的直方圖計(jì)算函數(shù)np.histogram也實(shí)現(xiàn)了相同的效果。如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • 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)??
  • 效果圖和上面的一個(gè)相同。NumPy的histogram函數(shù)將在NumPy通用函數(shù)這篇博文中介紹,這里就不詳細(xì)解釋了。這里采用的是與一開(kāi)始相同的比例系數(shù)的方法,參考本文的第二節(jié)。

    另外,通過(guò)NumPy和matplotlib可以更方便的繪制出直方圖,下面的代碼供大家參考,如果有機(jī)會(huì),再寫的專門介紹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、形態(tài)學(xué)處理)

    提示:

    • 轉(zhuǎn)載請(qǐng)?jiān)敿?xì)注明原作者及出處,謝謝!
    • 本文介紹使用OpenCV-Python進(jìn)行形態(tài)學(xué)處理
    • 本文不介紹形態(tài)學(xué)處理的基本概念,所以讀者需要預(yù)先對(duì)其有一定的了解。

    定義結(jié)構(gòu)元素

    形態(tài)學(xué)處理的核心就是定義結(jié)構(gòu)元素,在OpenCV-Python中,可以使用其自帶的getStructuringElement函數(shù),也可以直接使用NumPy的ndarray來(lái)定義一個(gè)結(jié)構(gòu)元素。首先來(lái)看用getStructuringElement函數(shù)定義一個(gè)結(jié)構(gòu)元素:

    [python]?view plaincopy
  • element?=?cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))??
  • 這就定義了一個(gè)5×5的十字形結(jié)構(gòu)元素,如下:

    也可以用NumPy來(lái)定義結(jié)構(gòu)元素,如下:

    [python]?view plaincopy
  • NpKernel?=?np.uint8(np.zeros((5,5)))??
  • for?i?in?range(5):??
  • ????NpKernel[2,?i]?=?1?#感謝chenpingjun1990的提醒,現(xiàn)在是正確的??
  • ????NpKernel[i,?2]?=?1??
  • 這兩者方式定義的結(jié)構(gòu)元素完全一樣: [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內(nèi)置的常量定義橢圓(MORPH_ELLIPSE)和十字形結(jié)構(gòu)(MORPH_CROSS)元素要簡(jiǎn)單一些,如果定義矩形(MORPH_RECT)和自定義結(jié)構(gòu)元素,則兩者差不多。

    本篇文章將用參考資料1中的相關(guān)章節(jié)的圖片做測(cè)試:


    腐蝕和膨脹

    下面先以腐蝕圖像為例子介紹如何使用結(jié)構(gòu)元素:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np??
  • ???
  • img?=?cv2.imread('D:/binary.bmp',0)??
  • #OpenCV定義的結(jié)構(gòu)元素??
  • 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)??
  • ??
  • #NumPy定義的結(jié)構(gòu)元素??
  • NpKernel?=?np.uint8(np.ones((3,3)))??
  • Nperoded?=?cv2.erode(img,NpKernel)??
  • #顯示腐蝕后的圖像??
  • cv2.imshow("Eroded?by?NumPy?kernel",Nperoded);??
  • ??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 如上所示,腐蝕和膨脹的處理很簡(jiǎn)單,只需設(shè)置好結(jié)構(gòu)元素,然后分別調(diào)用cv2.erode(...)和cv2.dilate(...)函數(shù)即可,其中第一個(gè)參數(shù)是需要處理的圖像,第二個(gè)是結(jié)構(gòu)元素。返回處理好的圖像。

    結(jié)果如下:


    開(kāi)運(yùn)算和閉運(yùn)算

    了解形態(tài)學(xué)基本處理的同學(xué)都知道,開(kāi)運(yùn)算和閉運(yùn)算就是將腐蝕和膨脹按照一定的次序進(jìn)行處理。但這兩者并不是可逆的,即先開(kāi)后閉并不能得到原先的圖像。代碼示例如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np??
  • ???
  • img?=?cv2.imread('D:/binary.bmp',0)??
  • #定義結(jié)構(gòu)元素??
  • kernel?=?cv2.getStructuringElement(cv2.MORPH_RECT,(5,?5))??
  • ??
  • #閉運(yùn)算??
  • closed?=?cv2.morphologyEx(img,?cv2.MORPH_CLOSE,?kernel)??
  • #顯示腐蝕后的圖像??
  • cv2.imshow("Close",closed);??
  • ??
  • #開(kāi)運(yùn)算??
  • opened?=?cv2.morphologyEx(img,?cv2.MORPH_OPEN,?kernel)??
  • #顯示腐蝕后的圖像??
  • cv2.imshow("Open",?opened);??
  • ??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??

  • 閉運(yùn)算用來(lái)連接被誤分為許多小塊的對(duì)象,而開(kāi)運(yùn)算用于移除由圖像噪音形成的斑點(diǎn)。因此,某些情況下可以連續(xù)運(yùn)用這兩種運(yùn)算。如對(duì)一副二值圖連續(xù)使用閉運(yùn)算和開(kāi)運(yùn)算,將獲得圖像中的主要對(duì)象。同樣,如果想消除圖像中的噪聲(即圖像中的“小點(diǎn)”),也可以對(duì)圖像先用開(kāi)運(yùn)算后用閉運(yùn)算,不過(guò)這樣也會(huì)消除一些破碎的對(duì)象。

    對(duì)原始圖像進(jìn)行開(kāi)運(yùn)算和閉運(yùn)算的結(jié)果如下:


    用形態(tài)學(xué)運(yùn)算檢測(cè)邊和角點(diǎn)

    這里通過(guò)一個(gè)較復(fù)雜的例子介紹如何用形態(tài)學(xué)算子檢測(cè)圖像中的邊緣和拐角(這里只是作為介紹形態(tài)學(xué)處理例子,實(shí)際使用時(shí)請(qǐng)用Canny或Harris等算法)。

    檢測(cè)邊緣

    形態(tài)學(xué)檢測(cè)邊緣的原理很簡(jiǎn)單,在膨脹時(shí),圖像中的物體會(huì)想周圍“擴(kuò)張”;腐蝕時(shí),圖像中的物體會(huì)“收縮”。比較這兩幅圖像,由于其變化的區(qū)域只發(fā)生在邊緣。所以這時(shí)將兩幅圖像相減,得到的就是圖像中物體的邊緣。這里用的依然是參考資料1中相關(guān)章節(jié)的圖片:

    代碼如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy??
  • ??
  • image?=?cv2.imread("D:/building.jpg",0);??
  • #構(gòu)造一個(gè)3×3的結(jié)構(gòu)元素???
  • element?=?cv2.getStructuringElement(cv2.MORPH_RECT,(3,?3))??
  • dilate?=?cv2.dilate(image,?element)??
  • erode?=?cv2.erode(image,?element)??
  • ??
  • #將兩幅圖像相減獲得邊,第一個(gè)參數(shù)是膨脹后的圖像,第二個(gè)參數(shù)是腐蝕后的圖像??
  • result?=?cv2.absdiff(dilate,erode);??
  • ??
  • #上面得到的結(jié)果是灰度圖,將其二值化以便更清楚的觀察結(jié)果??
  • retval,?result?=?cv2.threshold(result,?40,?255,?cv2.THRESH_BINARY);???
  • #反色,即對(duì)二值圖每個(gè)像素取反??
  • result?=?cv2.bitwise_not(result);???
  • #顯示圖像??
  • cv2.imshow("result",result);???
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 處理結(jié)果如下:

    檢測(cè)拐角

    與邊緣檢測(cè)不同,拐角的檢測(cè)的過(guò)程稍稍有些復(fù)雜。但原理相同,所不同的是先用十字形的結(jié)構(gòu)元素膨脹像素,這種情況下只會(huì)在邊緣處“擴(kuò)張”,角點(diǎn)不發(fā)生變化。接著用菱形的結(jié)構(gòu)元素腐蝕原圖像,導(dǎo)致只有在拐角處才會(huì)“收縮”,而直線邊緣都未發(fā)生變化。

    第二步是用X形膨脹原圖像,角點(diǎn)膨脹的比邊要多。這樣第二次用方塊腐蝕時(shí),角點(diǎn)恢復(fù)原狀,而邊要腐蝕的更多。所以當(dāng)兩幅圖像相減時(shí),只保留了拐角處。示意圖如下(示意圖來(lái)自參考資料1):

    代碼如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • ??
  • image?=?cv2.imread("D:/building.jpg",?0)??
  • origin?=?cv2.imread("D:/building.jpg")??
  • #構(gòu)造5×5的結(jié)構(gòu)元素,分別為十字形、菱形、方形和X型??
  • cross?=?cv2.getStructuringElement(cv2.MORPH_CROSS,(5,?5))??
  • #菱形結(jié)構(gòu)元素的定義稍麻煩一些??
  • 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))??
  • #使用cross膨脹圖像??
  • result1?=?cv2.dilate(image,cross)??
  • #使用菱形腐蝕圖像??
  • result1?=?cv2.erode(result1,?diamond)??
  • ??
  • #使用X膨脹原圖像???
  • result2?=?cv2.dilate(image,?x)??
  • #使用方形腐蝕圖像???
  • result2?=?cv2.erode(result2,square)??
  • ??
  • #result?=?result1.copy()??
  • #將兩幅閉運(yùn)算的圖像相減獲得角???
  • result?=?cv2.absdiff(result2,?result1)??
  • #使用閾值獲得二值圖??
  • retval,?result?=?cv2.threshold(result,?40,?255,?cv2.THRESH_BINARY)??
  • ??
  • #在原圖上用半徑為5的圓圈將點(diǎn)標(biāo)出。??
  • 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中函數(shù)參數(shù)中使用的坐標(biāo)系和NumPy的ndarray的坐標(biāo)系是不同的,在46行可以看出來(lái)。 抽空我向OpenCV郵件列表提一下,看我的理解是不是正確的。

    大家可以驗(yàn)證一下,比如在代碼中插入這兩行代碼,就能知道結(jié)果了:

    [python]?view plaincopy
  • cv2.circle(image,?(5,?10),?5,?(255,0,0))??
  • image[5,?10]?=?0??
  • 通過(guò)上面的代碼就能檢測(cè)到圖像中的拐角并標(biāo)出來(lái),效果圖如下:



    當(dāng)然,這只是個(gè)形態(tài)學(xué)處理示例,檢測(cè)結(jié)果并不好。

    未完待續(xù)...

    在將來(lái)的某一篇文章中將做個(gè)總結(jié),介紹下OpenCV中常用的函數(shù),如threshold、bitwise_xxx,以及繪制函數(shù)等。

    參考資料:

    1、《Opencv2 Computer Vision Application Programming Cookbook》

    2、《OpenCV References Manule》

    如果覺(jué)得本文寫的還可以的話,請(qǐng)輕點(diǎn)“頂”,方便讀者、以及您的支持是我寫下去的最大的兩個(gè)動(dòng)力。





    OpenCV-Python教程(5、初級(jí)濾波內(nèi)容)


    本篇文章介紹如何用OpenCV-Python來(lái)實(shí)現(xiàn)初級(jí)濾波功能。

    提示:

    • 轉(zhuǎn)載請(qǐng)?jiān)敿?xì)注明原作者及出處,謝謝!
    • 本文介紹使用OpenCV-Python實(shí)現(xiàn)基本的濾波處理
    • 本文不介紹濾波處理的詳細(xì)概念,所以讀者需要預(yù)先對(duì)其有一定的了解。

    簡(jiǎn)介

    過(guò)濾是信號(hào)和圖像處理中基本的任務(wù)。其目的是根據(jù)應(yīng)用環(huán)境的不同,選擇性的提取圖像中某些認(rèn)為是重要的信息。過(guò)濾可以移除圖像中的噪音、提取感興趣的可視特征、允許圖像重采樣,等等。其源自于一般的信號(hào)和系統(tǒng)理論,這里將不介紹該理論的細(xì)節(jié)。但本章會(huì)介紹關(guān)于過(guò)濾的基本概念,以及如何在圖像處理程序中使用濾波器。首先,簡(jiǎn)要介紹下頻率域分析的概念。

    當(dāng)我們觀察一張圖片時(shí),我們觀察的是圖像中有多少灰度級(jí)(或顏色)及其分布。根據(jù)灰度分布的不同來(lái)區(qū)分不同的圖像。但還有其他方面可以對(duì)圖像進(jìn)行分析。我們可以觀察圖像中灰度的變化。某些圖像中包含大量的強(qiáng)度不變的區(qū)域(如藍(lán)天),而在其他圖像中的灰度變化可能會(huì)非常快(如包含許多小物體的擁擠的圖像)。因此,觀察圖像中這些變化的頻率就構(gòu)成了另一條分類圖像的方法。這個(gè)觀點(diǎn)稱為頻域。而通過(guò)觀察圖像灰度分布來(lái)分類圖像稱為空間域。

    頻域分析將圖像分成從低頻到高頻的不同部分。低頻對(duì)應(yīng)圖像強(qiáng)度變化小的區(qū)域,而高頻是圖像強(qiáng)度變化非常大的區(qū)域。目前已存在若干轉(zhuǎn)換方法,如傅立葉變換或余弦變換,可以用來(lái)清晰的顯示圖像的頻率內(nèi)容。注意,由于圖像是一個(gè)二維實(shí)體,所以其由水平頻率(水平方向的變化)和豎直頻率(豎直方向的變化)共同組成。

    在頻率分析領(lǐng)域的框架中,濾波器是一個(gè)用來(lái)增強(qiáng)圖像中某個(gè)波段或頻率并阻塞(或降低)其他頻率波段的操作。低通濾波器是消除圖像中高頻部分,但保留低頻部分。高通濾波器消除低頻部分

    本篇文章介紹在OpenCV-Python中實(shí)現(xiàn)的初級(jí)的濾波操作,下一篇文章介紹更加復(fù)雜的濾波原理及其實(shí)現(xiàn)。

    本篇文章使用傳統(tǒng)的lena作為實(shí)驗(yàn)圖像。

    用低通濾波來(lái)平滑圖像

    低通濾波器的目標(biāo)是降低圖像的變化率。如將每個(gè)像素替換為該像素周圍像素的均值。這樣就可以平滑并替代那些強(qiáng)度變化明顯的區(qū)域。在OpenCV中,可以通過(guò)blur函數(shù)做到這一點(diǎn):

    [python]?view plaincopy
  • dst?=?cv2.blur(image,(5,5));???
  • 其中dst是blur處理后返回的圖像,參數(shù)一是輸入的待處理圖像,參數(shù)2是低通濾波器的大小。其后含有幾個(gè)可選參數(shù),用來(lái)設(shè)置濾波器的細(xì)節(jié),具體可查閱參考資料2。不過(guò)這里,這樣就夠了。下面是一個(gè)簡(jiǎn)單的示例代碼:

    [python]?view plaincopy
  • #coding=utf-8??
  • 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()??
  • 結(jié)果如下,左邊是平滑過(guò)的圖像,右邊是原圖像:


    這種濾波器又稱為boxfilter(注,這與化學(xué)上的箱式過(guò)濾器是兩碼事,所以這里就不翻譯了)。所以也可通過(guò)OpenCV的cv2.bofxfilter(...)函數(shù)來(lái)完成相同的工作。如下:

    [python]?view plaincopy
  • result1?=?cv2.boxFilter(img,?-1,?(5,?5))??
  • 這行代碼與上面使用blur函數(shù)的效果完全相同。其中第二個(gè)參數(shù)的-1表示輸出圖像使用的深度與輸入圖像相同。后面還有幾個(gè)可選參數(shù),具體可查閱OpenCV文檔。

    高斯模糊

    在某些情況下,需要對(duì)一個(gè)像素的周圍的像素給予更多的重視。因此,可通過(guò)分配權(quán)重來(lái)重新計(jì)算這些周圍點(diǎn)的值。這可通過(guò)高斯函數(shù)(鐘形函數(shù),即喇叭形數(shù))的權(quán)重方案來(lái)解決。cv::GaussianBlur函數(shù)可作為濾波器用下面的方法調(diào)用:

    [python]?view plaincopy
  • gaussianResult?=?cv2.GaussianBlur(img,(5,5),1.5)??
  • 區(qū)別

    低通濾波與高斯濾波的不同之處在于:低通濾波中,濾波器中每個(gè)像素的權(quán)重是相同的,即濾波器是線性的。而高斯濾波器中像素的權(quán)重與其距中心像素的距離成比例。關(guān)于高斯模糊的詳細(xì)內(nèi)容,抽空將寫一篇獨(dú)立的文章介紹。

    使用中值濾波消除噪點(diǎn)

    前面介紹的是線性過(guò)濾器,這里介紹非線性過(guò)濾器——中值濾波器。由于中值濾波器對(duì)消除椒鹽現(xiàn)象特別有用。所以我們使用第二篇教程中椒鹽函數(shù)先對(duì)圖像進(jìn)行處理,將處理結(jié)果作為示例圖片。

    調(diào)用中值濾波器的方法與調(diào)用其他濾波器的方法類似,如下:

    [python]?view plaincopy
  • result?=?cv2.medianBlur(image,5)??
  • 函數(shù)返回處理結(jié)果,第一個(gè)參數(shù)是待處理圖像,第二個(gè)參數(shù)是孔徑的尺寸,一個(gè)大于1的奇數(shù)。比如這里是5,中值濾波器就會(huì)使用5×5的范圍來(lái)計(jì)算。即對(duì)像素的中心值及其5×5鄰域組成了一個(gè)數(shù)值集,對(duì)其進(jìn)行處理計(jì)算,當(dāng)前像素被其中值替換掉。

    如果在某個(gè)像素周圍有白色或黑色的像素,這些白色或黑色的像素不會(huì)選擇作為中值(最大或最小值不用),而是被替換為鄰域值。代碼如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • 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)??
  • 處理結(jié)果如下:

    由于中值濾波不會(huì)處理最大和最小值,所以就不會(huì)受到噪聲的影響。相反,如果直接采用blur進(jìn)行均值濾波,則不會(huì)區(qū)分這些噪聲點(diǎn),濾波后的圖像會(huì)受到噪聲的影響。

    中值濾波器在處理邊緣也有優(yōu)勢(shì)。但中值濾波器會(huì)清除掉某些區(qū)域的紋理(如背景中的樹(shù))。

    其他

    由于方向?yàn)V波器與這里的原理有較大的出入,所以將用獨(dú)立的一篇文章中介紹其原理以及實(shí)現(xiàn)。

    參考資料:

    1、《Opencv2 Computer Vision Application Programming Cookbook》

    2、《OpenCV References Manule》





    from: http://blog.csdn.net/sunny2038/article/category/904451

    總結(jié)

    以上是生活随笔為你收集整理的OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。