智能圖像處理:Python和OpenCV實(shí)現(xiàn)-課件 第十章 圖像分割_第1頁
智能圖像處理:Python和OpenCV實(shí)現(xiàn)-課件 第十章 圖像分割_第2頁
智能圖像處理:Python和OpenCV實(shí)現(xiàn)-課件 第十章 圖像分割_第3頁
智能圖像處理:Python和OpenCV實(shí)現(xiàn)-課件 第十章 圖像分割_第4頁
智能圖像處理:Python和OpenCV實(shí)現(xiàn)-課件 第十章 圖像分割_第5頁
已閱讀5頁,還剩57頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第十章圖像分割圖像分割(imagesegmentation)技術(shù)是計(jì)算機(jī)視覺領(lǐng)域的一個重要的研究方向,是圖像語義理解的重要一環(huán)。圖像分割是指將圖像分成若干具有相似性質(zhì)區(qū)域的過

程,從數(shù)學(xué)角度來看,圖像分割是將圖像劃分成互不相交區(qū)域的過程。圖像分割是一種基本的圖像處理技術(shù),是指將圖像分成不同特性的區(qū)域,并對目標(biāo)進(jìn)行提取的技術(shù),它是由圖像處理到圖像分析的關(guān)鍵步驟。目標(biāo)提取和圖像理解都是在圖像分割的基礎(chǔ)上進(jìn)行的。圖像分割就是把圖像分成若干個特定的、具有獨(dú)特性質(zhì)的區(qū)域并提取出感興趣目標(biāo)的技術(shù)和過程?,F(xiàn)有的圖像分割方法主要分以下幾類:基于閾值的分割方法、基于區(qū)域的分割方法、基于邊緣的分割方法以及基于特定理論的分割方法等。還有分成語義分割和實(shí)例分割。

圖像分割的過程也是一個標(biāo)記過程,即把屬于同一區(qū)域的像素賦予相同的編號。10.1圖像閾值分割

閾值分割法是一種最常用的并行區(qū)域技術(shù),它是圖像分割中應(yīng)用數(shù)量最多的一類。閾值分割方法實(shí)際上是輸入圖像f到輸出圖像g的如下變換:

閾值分割的優(yōu)點(diǎn)是計(jì)算簡單、運(yùn)算效率較高、速度快。在重視運(yùn)算效率的應(yīng)用場合應(yīng)用的比較廣泛。人們發(fā)展了各種各樣的閾值處理技術(shù),包括全局閾值、自適應(yīng)閾值、最佳閾值等。全局閾值

是指整幅圖像使用同一個閾值做分割處理,適用于背景和前景有明顯對比的圖像,它是根據(jù)整幅圖像確定的。但是這種方法只考慮像素本身的灰度值,一般不考慮空間特征,因而對噪聲很敏感。常用的全局閾值選取方法有利用圖像灰度直方圖的峰谷法、最小誤差法、最大類間方差法、最大熵自動閾值法以及其它一些方法。自適應(yīng)閾值是指在許多情況下,物體和背景的對比度在圖像中的各處是不一樣的,這時很難用一個統(tǒng)一的閾值將物體與背景分開。這時可以根據(jù)圖像的局部特征分別采用不同的閾值進(jìn)行分割。實(shí)際處理時,需要按照具體問題將圖像分成若干子區(qū)域分別選擇閾值,或者動態(tài)地根據(jù)一定的鄰域范圍選擇每點(diǎn)處的閾值,進(jìn)行圖像分割。最佳閾值閾值的選擇需要根據(jù)具體問題來確定,一般通過實(shí)驗(yàn)來確定。對于給定的圖像,可以通過分析直方圖的方法確定最佳的閾值,例如當(dāng)直方圖明顯呈現(xiàn)雙峰情況時,可以選擇兩個峰值的中點(diǎn)作為最佳閾值。10.1.1全局閾值分割全局閾值分割即當(dāng)像素值高于某一閾值時(如127),我們給這個像素賦予一個新值(如白色),否則我們給它賦予另外一種顏色(如黑色)。OpenCV中的實(shí)現(xiàn)全局閾值分割的函數(shù)是cv2.threshold,其語法格式為:dst=cv2.threshold(src,thresh,maxval,type)其中輸入輸出參數(shù)如下:src:源圖像,必須是單通道;thresh:閾值,取值范圍0~255;maxval:填充色,取值范圍0~255;type:閾值類型如表10-1所示。表10-1閾值類型type參數(shù)類

型含

義cv2.THRESH_BINARY二進(jìn)制閾值化,非黑即白cv2.THRESH_BINARY_INV反二進(jìn)制閾值化,非白即黑cv2.THRESH_TRUNC截?cái)嚅撝祷?,大于閾值設(shè)為1cv2.THRESH_TOZERO閾值化為0,小于閾值設(shè)為0cv2.THRESH_TOZERO_INV反閾值化為0,大于閾值設(shè)為0【例10.1】使用各種閾值分割類型對圖像進(jìn)行處理,程序代碼如下:importcv2frommatplotlibimportpyplotasplt

img=cv2.imread('d:/pics/lena.jpg',0)ret,thresh1=cv2.threshold(img,127,255,cv2.THRESH_BINARY)ret,thresh2=cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)ret,thresh3=cv2.threshold(img,127,255,cv2.THRESH_TRUNC)ret,thresh4=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)ret,thresh5=cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)

titles=['OriginalImage','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']images=[img,thresh1,thresh2,thresh3,thresh4,thresh5]foriinrange(6):plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')plt.title(titles[i])plt.xticks([]),plt.yticks([])plt.show()程序運(yùn)行結(jié)果如圖10-1所示。圖10-1各種閾值類型處理的圖像10.1.2自適應(yīng)閾值上節(jié)介紹的閾值算法是全局閾值,但一副圖像中不同位置的光照情況可能不同,全局閾值會失去很多信息,這種情況下我們需要采用自適應(yīng)閾值。自適應(yīng)閾值二值化函數(shù)根據(jù)圖像的小塊區(qū)域的值來計(jì)算對應(yīng)區(qū)域的閾值,從而得到更為合適的圖像。OpenCV中實(shí)現(xiàn)自適應(yīng)閾值分割的函數(shù)是cv2.adaptiveThreshold,其語法格式為:dst=cv2.adaptiveThreshold(src,maxval,thresh_type,type,BlockSize,C)其中輸入輸出參數(shù)如下:src:源圖像,必須是單通道;dst:輸出圖像;maxval:填充色,取值范圍0~255;thresh_type:計(jì)算閾值的方法有如下2種:cv2.ADAPTIVE_THRESH_MEAN_C:通過平均的方法取得平均值;cv2.ADAPTIVE_THRESH_GAUSSIAN_C:通過高斯法取得高斯值。type;閾值類型,見表10-1所示;BlockSize:圖片中分塊的大小;C:閾值計(jì)算方法中的常數(shù)項(xiàng)?!纠?0.2】使用自適應(yīng)選取閾值方法對圖像進(jìn)行處理,程序代碼如下:importcv2importnumpyasnpfrommatplotlibimportpyplotasplt

img=cv2.imread('d:/pics/lena.jpg',0)ret,th1=cv2.threshold(img,127,255,cv2.THRESH_BINARY)#設(shè)Blocksize=11,C=2th2=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,2)th3=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)titles=['OriginalImage','GlobalThresholding(v=127)','AdaptiveMean','AdaptiveGaussian']

images=[img,th1,th2,th3]foriinrange(4):plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')plt.title(titles[i]),plt.axis('off')plt.show()程序運(yùn)行結(jié)果如圖10-2所示圖10-2自適應(yīng)閾值方法處理圖像10.1.3Otsu’s二值化(大津閾值分割法)Otsu’s二值化(也稱為大津閾值分割法或最大類間方差)是對一副雙峰圖像自動根據(jù)其直方圖計(jì)算出一個閾值。但對于非雙峰圖像,這種方法得到的結(jié)果可能會不理想。Otsu’s二值化使用的函數(shù)是cv2.threshold(),但是需要多傳入一個參數(shù)flag:cv2.THRESH_OTSU,這時要把閾值設(shè)為0,算法會自動找到最優(yōu)閾值。這個最優(yōu)閾值就是返回值retVal。如果不使用Otsu’s二值化,返回的retVal值與設(shè)定的閾值相等。計(jì)算圖像直方圖;設(shè)定閾值,把直方圖強(qiáng)度大于閾值的像素分成一組,把小于閾值的像素分成另外一組;分別計(jì)算兩組內(nèi)的偏移數(shù),并把偏移數(shù)相加。

把0~255依照順序設(shè)為閾值,重復(fù)1-3的步驟,直到得到最小偏移數(shù),其所對應(yīng)的值即為最終閾值。Otsus圖像分割步驟:【例10.3】使用Otsu’s二值化求取閾值,對圖像進(jìn)行分割,程序代碼如下:importcv2frommatplotlibimportpyplotaspltimg=cv2.imread('d:/pics/lenasp.jpg',0)#讀取帶有椒鹽噪聲的圖像ret1,th1=cv2.threshold(img,127,255,cv2.THRESH_BINARY)#全局閾值ret2,th2=cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)#Otsu's閾值

#先高斯濾波,高斯核的大小為(5,5),標(biāo)準(zhǔn)差為0blur=cv2.GaussianBlur(img,(5,5),0)#再進(jìn)行Otsu's閾值ret3,th3=cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

#顯示圖像及直方圖images=[img,0,th1,img,0,th2,blur,0,th3]titles=['Origin_Noisy_img','Histogram','GlobalThresholding(v=127)','Origin_Noisy_img','Histogram',"Otsu'sThresholding",'Gaussian_filtered_img','Histogram',"Otsu'sThresholding"]foriinrange(3):plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')plt.title(titles[i*3]),plt.axis('off')plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)plt.title(titles[i*3+1])plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')plt.title(titles[i*3+2]),plt.axis('off')plt.show()程序運(yùn)行結(jié)果如圖10-3所示圖10-3Otsu’s二值化圖像分割10.2圖像區(qū)域分割區(qū)域分割的方法有區(qū)域生長和區(qū)域分裂合并法,兩個都是典型的串行區(qū)域技術(shù),其分割過程后續(xù)步驟的處理要根據(jù)前面步驟的結(jié)果進(jìn)行判斷而確定。10.2.1區(qū)域生長區(qū)域生長的基本思想是將具有相似性質(zhì)的像素集合起來構(gòu)成區(qū)域。具體先對每個需要分割的區(qū)域找一個種子像素作為生長的起點(diǎn),然后將種子像素周圍鄰域中與種子像素有相同或相似性質(zhì)的像素(根據(jù)某種事先確定的生長或相似準(zhǔn)則來判定)合并到種子像素所在的區(qū)域中。將這些新像素當(dāng)作新的種子像素繼續(xù)進(jìn)行上面的過程,直到再沒有滿足條件的像素可被包括進(jìn)來,這樣一個區(qū)域就長成了。區(qū)域生長需要選擇一組能正確代表所需區(qū)域的種子像素,確定在生長過程中的相似性準(zhǔn)則,制定讓生長停止的條件或準(zhǔn)則。相似性準(zhǔn)則可以是灰度級、彩色、紋理、梯度等特性。選取的種子像素可以是單個像素,也可以是包含若干個像素的小區(qū)域。大部分區(qū)域生長準(zhǔn)則使用圖像的局部性質(zhì),生長準(zhǔn)則可根據(jù)不同原則制定,而使用不同的生長準(zhǔn)則會影響區(qū)域生長的過程。區(qū)域生長法的優(yōu)點(diǎn)是計(jì)算簡單,對于較均勻的連通目標(biāo)有較好的分割效果。它的缺點(diǎn)是需要人為確定種子點(diǎn),對噪聲敏感,可能導(dǎo)致區(qū)域內(nèi)有空洞。另外,它是一種串行區(qū)域分割圖像的分割方法,當(dāng)目標(biāo)較大時,分割速度較慢,因此在設(shè)計(jì)算法時,要盡量提高效率。區(qū)域生長是指從某個像素出發(fā),按照一定的準(zhǔn)則,逐步加入鄰近像素,當(dāng)滿足一定的條件時,區(qū)域生長終止,最后得到整個區(qū)域,進(jìn)而實(shí)現(xiàn)目標(biāo)的提取。區(qū)域生長的好壞決定于①初始點(diǎn)(種子點(diǎn))的選??;②生長準(zhǔn)則;③終止條件。區(qū)域生長實(shí)現(xiàn)的具體步驟如下:1)對圖像順序掃描。找到第1個還沒有歸屬的像素,設(shè)該像素為(x0,y0);2)以(x0,y0)為中心,考慮(x0,y0)的4鄰域像素(x,y)。如果(x0,y0)滿足生長準(zhǔn)則,將(x,y)與(x0,y0)合并(在同一區(qū)域內(nèi)),同時將(x,y)壓入堆棧;3)從堆棧中取出一個像素,把它當(dāng)作(x0,y0)返回到步驟2;4)當(dāng)堆棧為空時返回到步驟1;5)重復(fù)步驟1—4直到圖像中的每個點(diǎn)都有歸屬時,生長結(jié)束。【10.4】基于初始種子自動選取的區(qū)域生長。程序代碼如下:importcv2importnumpyasnp

#初始種子選擇deforiginalSeed(gray,th):ret,thresh=cv2.threshold(gray,th,250,cv2.THRESH_BINARY)#二值圖,種子區(qū)域(不同劃分可獲得不同種子)kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))#3×3結(jié)構(gòu)元thresh_copy=thresh.copy()thresh_B=np.zeros(gray.shape,np.uint8)

seeds=[]#為了記錄種子坐標(biāo)#循環(huán),直到thresh_copy中的像素值全部為0whilethresh_copy.any():Xa_copy,Ya_copy=np.where(thresh_copy>0) #thresh_A_copy中值為255的像素的坐標(biāo)thresh_B[Xa_copy[0],Ya_copy[0]]=255#選取第一個點(diǎn),并將thresh_B中對應(yīng)像素值改為255#連通分量算法,先對thresh_B進(jìn)行膨脹,再和thresh執(zhí)行“與”操作(取交集)foriinrange(200):dilation_B=cv2.dilate(thresh_B,kernel,iterations=1)thresh_B=cv2.bitwise_and(thresh,dilation_B)

#取thresh_B值為255的像素坐標(biāo),并將thresh_copy中對應(yīng)坐標(biāo)像素值變?yōu)?Xb,Yb=np.where(thresh_B>0)thresh_copy[Xb,Yb]=0

#循環(huán),在thresh_B中只有一個像素點(diǎn)時停止whilestr(thresh_B.tolist()).count("255")>1:thresh_B=cv2.erode(thresh_B,kernel,iterations=1)#腐蝕操作

X_seed,Y_seed=np.where(thresh_B>0)#取此處種子坐標(biāo)ifX_seed.size>0andY_seed.size>0:seeds.append((X_seed[0],Y_seed[0]))#將種子坐標(biāo)寫入seedsthresh_B[Xb,Yb]=0#將thresh_B像素值置零returnseeds

#區(qū)域生長defregionGrow(gray,seeds,thresh,p):seedMark=np.zeros(gray.shape)ifp==8:#八鄰域connection=[(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1)]elifp==4:#四鄰域connection=[(-1,0),(0,1),(1,0),(0,-1)]

#seeds內(nèi)無元素時候生長停止whilelen(seeds)!=0:#棧頂元素出棧pt=seeds.pop(0)foriinrange(p):tmpX=pt[0]+connection[i][0]tmpY=pt[1]+connection[i][1]

#檢測邊界點(diǎn)iftmpX<0ortmpY<0ortmpX>=gray.shape[0]ortmpY>=gray.shape[1]:continueifabs(int(gray[tmpX,tmpY])-int(gray[pt]))<threshandseedMark[tmpX,tmpY]==0:seedMark[tmpX,tmpY]=255seeds.append((tmpX,tmpY))returnseedMark

if__name__=='__main__':img=cv2.imread("d:/pics/rice.png")gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)cv2.imshow('Originalimage',gray)seeds=originalSeed(gray,th=180)seedMark=regionGrow(gray,seeds,thresh=3,p=8)cv2.imshow("seedMark",seedMark)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-4所示(a)原圖像

(b)區(qū)域生長結(jié)果圖10.2.2區(qū)域分裂合并區(qū)域分裂合并基本上是區(qū)域生長的逆過程:從整個圖像出發(fā),不斷分裂得到各個子區(qū)域,然后再把前景區(qū)域合并,實(shí)現(xiàn)目標(biāo)提取。分裂合并的假設(shè)是對于一幅圖像,前景區(qū)域由一些相互連通的像素組成的,因此,如果把一幅圖像分裂到像素級,那么就可以判定該像素是否為前景像素。當(dāng)所有像素點(diǎn)或者子區(qū)域完成判斷以后,把前景區(qū)域或者像素合并就可得到前景目標(biāo)。區(qū)域分裂合并方法中,最常用的方法是四叉樹分解法,如圖10-5所示。分裂合并法的關(guān)鍵是分裂合并準(zhǔn)則的設(shè)計(jì),其基本思想是先確定一個分裂合并的準(zhǔn)則,即區(qū)域特征一致性的測度。當(dāng)圖像中某個區(qū)域的特征不一致時就將該區(qū)域分裂成4個相等的子區(qū)域,當(dāng)分裂到不能再分的情況時,分裂結(jié)束;然后查找相鄰區(qū)域有沒有相似的特征,當(dāng)相鄰的子區(qū)域滿足一致性特征時則將它們合成一個大區(qū)域,直至所有區(qū)域不再滿足分裂合并的條件為止,最后達(dá)到分割的目的。在一定程度上區(qū)域生長和區(qū)域分裂合并算法有異曲同工之妙,互相促進(jìn)相輔相成的,區(qū)域分裂到極致就是分割成單一像素點(diǎn),然后按照一定的合并準(zhǔn)則進(jìn)行合并,在一定程度上可以認(rèn)為是單一像素點(diǎn)的區(qū)域生長方法。區(qū)域生長比區(qū)域分裂合并的方法節(jié)省了分裂的過程,而區(qū)域分裂合并的方法可以在較大的一個相似區(qū)域基礎(chǔ)上再進(jìn)行相似合并,而區(qū)域生長只能從單一像素點(diǎn)出發(fā)進(jìn)行生長(合并)。區(qū)域分裂合并方法對復(fù)雜圖像的分割效果較好,但算法較復(fù)雜,計(jì)算量大,分裂還可能破壞區(qū)域的邊界。圖10-5四叉樹分解圖【例10.5】利用區(qū)域分裂合并算法分割圖像,程序代碼如下:importnumpyasnpimportcv2

#判斷方框是否需要再次拆分為四個defjudge(w0,h0,w,h):a=img[h0:h0+h,w0:w0+w]ave=np.mean(a)std=np.std(a,ddof=1)count=0total=0foriinrange(w0,w0+w):forjinrange(h0,h0+h):ifabs(img[j,i]-ave)<1*std:count+=1total+=1if(count/total)<0.95:#合適的點(diǎn)還是比較少,接著拆returnTrueelse:returnFalse##將圖像將根據(jù)閾值二值化處理,在此默認(rèn)125defdraw(w0,h0,w,h):foriinrange(w0,w0+w):forjinrange(h0,h0+h):ifimg[j,i]>125:img[j,i]=255else:img[j,i]=0defsplitting(w0,h0,w,h):#分裂函數(shù)ifjudge(w0,h0,w,h)and(min(w,h)>5):splitting(w0,h0,int(w/2),int(h/2))splitting(w0+int(w/2),h0,int(w/2),int(h/2))splitting(w0,h0+int(h/2),int(w/2),int(h/2))splitting(w0+int(w/2),h0+int(h/2),int(w/2),int(h/2))else:draw(w0,h0,w,h)if__name__=="__main__":img=cv2.imread('d:/pics/lena.jpg',0)img_input=cv2.imread('d:/pics/lena.jpg',0)#備份height,width=img.shape[0:2]splitting(0,0,width,height)cv2.imshow('input',img_input)cv2.imshow('output',img)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-6所示(a)原圖像(b)區(qū)域分裂合并圖像圖10-6區(qū)域分裂合并算法分割圖像10.3圖像的邊緣分割

圖像分割的一種重要途徑是通過邊緣檢測,即檢測灰度級或者結(jié)構(gòu)具有突變的地方,表明一個區(qū)域的終結(jié),也是另一個區(qū)域開始的地方。圖像中邊緣處像素的灰度值不連續(xù),這種不連續(xù)性可通過求導(dǎo)數(shù)來檢測到。對于階躍狀邊緣,其位置對應(yīng)一階導(dǎo)數(shù)的極值點(diǎn),對應(yīng)二階導(dǎo)數(shù)的過零點(diǎn)(零交叉點(diǎn))。因此常用微分算子進(jìn)行邊緣檢測。常用的一階微分算子有Roberts算子、Prewitt算子和Sobel算子,二階微分算子有Laplace算子和Kirsh算子等。在實(shí)際中各種微分算子常用小區(qū)域模板來表示,微分運(yùn)算是利用模板和圖像卷積來實(shí)現(xiàn)。這些算子對噪聲敏感,只適合于噪聲較小不太復(fù)雜的圖像。由于邊緣和噪聲都是灰度不連續(xù)點(diǎn),在頻域均為高頻分量,直接采用微分運(yùn)算難以克服噪聲的影響。因此用微分算子檢測邊緣前要對圖像進(jìn)行平滑濾波。LoG算子和Canny算子是具有平滑功能的二階和一階微分算子,邊緣檢測效果較好。其中LoG算子是采用Laplacian算子求高斯函數(shù)的二階導(dǎo)數(shù),Canny算子是高斯函數(shù)的一階導(dǎo)數(shù),它在噪聲抑制和邊緣檢測之間取得了較好的平衡?!纠?0.6】圖像的邊緣檢測、提取與分割,程序代碼如下:

importcv2#讀取原灰度圖片image=cv2.imread("d:/pics/lena.jpg",0)cv2.imshow("originimage",image)#圖像的閾值分割處理,即將圖像處理成非黑即白的二值圖像ret,image1=cv2.threshold(image,80,255,cv2.THRESH_BINARY)#binary(黑白二值),ret代表閾值,80是低閾值,255是高閾值cv2.imshow('binaryimage1',image1)#二值圖像的反色處理,將圖片像素取反height,width=image1.shape[0:2]#返回圖片大小image2=image1.copy()foriinrange(height):forjinrange(width):image2[i,j]=(255-image1[i,j])#cv2.imshow('inverseimage2',image2)#邊緣提取,使用Canny函數(shù)image2_3=cv2.Canny(image2,80,255)#設(shè)置80為低閾值,255為高閾值cv2.imshow('cannyimage',image2_3)

#再次對圖像進(jìn)行反色處理height1,width1=image2_3.shapeimage3=image2_3.copy()foriinrange(height1):forjinrange(width1):image3[i,j]=(255-image2_3[i,j])

cv2.imshow('inv_cannyimage',image3)cv2.waitKey(0) cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-7所示(a)原圖像(b)二值圖像(c)反色圖像(d)Canny邊緣反色圖像圖10-7圖像邊緣分割10.4直方圖分割法直方圖分割法要求目標(biāo)圖像和背景圖像的灰度級有著明顯的區(qū)別,并且該圖像的灰度直方圖有較明顯的雙峰。分割圖像的直方圖的雙峰分別代表目標(biāo)圖像和背景圖像,而波谷代表著分割圖像的邊緣。當(dāng)灰度級直方圖具有雙峰特性時,選取兩峰之間的谷對應(yīng)的灰度級作為閾值,可以得到較好的二值化圖像分割處理效果。與其他圖像分割方法相比,基于直方圖的方法是非常有效的圖像分割方法,是典型的全局單閾值分割方法。

【例10.7】直方圖閾值分割圖像,程序代碼如下:importcv2importnumpyasnp#計(jì)算灰度直方圖defcalcGrayHist(grayimage):rows,cols=grayimage.shape#print(grayimage.shape)#存儲灰度直方圖grayHist=np.zeros([256],np.uint64)forrinrange(rows):forcinrange(cols):grayHist[grayimage[r][c]]+=1returngrayHist#閾值分割:直方圖技術(shù)法defthreshTwoPeaks(image):iflen(image.shape)==2:gray=imageelse:gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

#計(jì)算灰度直方圖histogram=calcGrayHist(gray)#尋找灰度直方圖的最大峰值對應(yīng)的灰度值maxLoc=np.where(histogram==np.max(histogram))firstPeak=maxLoc[0][0]#尋找灰度直方圖的第二個峰值對應(yīng)的灰度值measureDists=np.zeros([256],np.float32)forkinrange(256):measureDists[k]=pow(k-firstPeak,2)*histogram[k]maxLoc2=np.where(measureDists==np.max(measureDists))secondPeak=maxLoc2[0][0]#找到兩個峰值之間的最小值對應(yīng)的灰度值,作為閾值thresh=0iffirstPeak>secondPeak:#第一個峰值再第二個峰值的右側(cè)temp=histogram[int(secondPeak):int(firstPeak)]minloc=np.where(temp==np.min(temp))thresh=secondPeak+minloc[0][0]+1else:#第一個峰值再第二個峰值的左側(cè)temp=histogram[int(firstPeak):int(secondPeak)]minloc=np.where(temp==np.min(temp))thresh=firstPeak+minloc[0][0]+1

#找到閾值之后進(jìn)行閾值處理,得到二值圖threshImage_out=gray.copy()#大于閾值的都設(shè)置為255threshImage_out[threshImage_out>thresh]=255threshImage_out[threshImage_out<=thresh]=0returnthresh,threshImage_outif__name__=="__main__":img=cv2.imread('d:/pics/leopard.png')thresh,out_img=threshTwoPeaks(img)print('thresh=',thresh)cv2.imshow('Orginalimage',img)cv2.imshow('Resultimage',out_img)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-8所示(a)原圖(b)自適應(yīng)閾值分割后的二值圖圖10-8直方圖閾值分割圖像可見,直方圖閾值分割法能夠較為有效的將背景和前景區(qū)分開來,比較完整的分割出圖片中的目標(biāo)物體。值得一提的是,對于任何一張圖像,它的直方圖中如果存在較為明顯的雙峰,用直方圖分割技術(shù)法可以達(dá)到很好的效果,否則,達(dá)到的效果會很不理想。10.5圖像連接組件標(biāo)記算法連接組件標(biāo)記算法(connectedcomponentlabelingalgorithm)是圖像分析中最常用的算法之一,算法的實(shí)質(zhì)是掃描二值圖像的每個像素點(diǎn),對于像素值相同的而且相互連通分為相同的組,最終得到圖像中所有的像素連通組件。OpenCV中計(jì)算圖像連接組件標(biāo)記的函數(shù)cv2.connectedComponents,其語法格式為:retval,labels=cv2.connectedComponents(image,connectivity,ltype)其中輸入輸出參數(shù)為::image:輸入二值圖像,黑色背景;connectivity:8連通域,默認(rèn)是8連通;ltype:輸出的labels類型,默認(rèn)是CV_32S輸出;retval,labels:輸出的標(biāo)記圖像,背景index=0。OpenCV中連通組件狀態(tài)統(tǒng)計(jì)函數(shù)語法格式為:retval,labels,stats,centroids=cv2.connectedComponentsWithStats(image,connectivity,ltype)其中相關(guān)的統(tǒng)計(jì)信息包括在輸出stats的對象中,每個連通組件有一個這樣的輸出結(jié)構(gòu)體:CC_STAT_LEFT:連通組件外接矩形左上角坐標(biāo)的X位置信息;CC_STAT_TOP:連通組件外接左上角坐標(biāo)的Y位置信息;CC_STAT_WIDTH:連通組件外接矩形寬度;CC_STAT_HEIGHT:連通組件外接矩形高度;CC_STAT_AREA:連通組件的面積大小,基于像素多少統(tǒng)計(jì);centroids輸出的是每個連通組件的中心位置坐標(biāo)(x,y)。【例10.8】利用連接組件標(biāo)記算法分割圖像,并用不同顏色標(biāo)記出來。程序代碼如下:importcv2importnumpyasnp

src=cv2.imread("d:/pics/lena.jpg")src=cv2.GaussianBlur(src,(3,3),0)gray=cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)ret,binary=cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)cv2.imshow("origin",src)cv2.imshow("binary",binary)#cv.imwrite('binary.png',binary)

output=cv2.connectedComponents(binary,connectivity=8,ltype=cv2.CV_32S)num_labels=output[0]#print(num_labels)#output:5labels=output[1]

#構(gòu)造顏色colors=[]foriinrange(num_labels):b=np.random.randint(0,256)g=np.random.randint(0,256)r=np.random.randint(0,256)colors.append((b,g,r))colors[0]=(0,0,0)

#畫出連通圖h,w=gray.shapeimage=np.zeros((h,w,3),dtype=np.uint8)forrowinrange(h):forcolinrange(w):image[row,col]=colors[labels[row,col]]

cv2.imshow("coloredlabels",image)print("totalcomponets:",num_labels-1)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-9所示(a)原圖像(b)二值化圖像(c)連通區(qū)域圖像圖10-9圖像連通區(qū)域標(biāo)記【例10.9】利用連通組件狀態(tài)統(tǒng)計(jì)函數(shù)統(tǒng)計(jì)信息,并標(biāo)記出來,可用于統(tǒng)計(jì)目標(biāo)的面積和數(shù)目。程序代碼如下:importcv2importnumpyasnpimg=cv2.imread("d:/pics/rice.png")img=cv2.GaussianBlur(img,(3,3),0)gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)ret,binary_=cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)cv2.imshow("Origin",img)#使用開運(yùn)算去掉外部的噪聲kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))binary=cv2.morphologyEx(binary_,cv2.MORPH_OPEN,kernel)#cv.imshow("binary",binary_)num_labels,labels,stats,centers=cv2.connectedComponentsWithStats(binary,connectivity=8,ltype=cv2.CV_32S)colors=[]foriinrange(num_labels):b=np.random.randint(0,256)g=np.random.randint(0,256)r=np.random.randint(0,256)colors.append((b,g,r))colors[0]=(0,0,0)image=np.copy(img)fortinrange(1,num_labels,1):x,y,w,h,area=stats[t]cx,cy=centers[t]#標(biāo)出中心位置cv2.circle(image,(32(cx),32(cy)),2,(0,255,0),2,8,0)#畫出外接矩形cv2.rectangle(image,(x,y),(x+w,y+h),colors[t],1,8,0)cv2.putText(image,"No."+str(t),(x,y),cv2.FONT_HERSHEY_SIMPLEX,.5,(0,0,255),1)print("labelindex%d,areaofthelabel:%d"%(t,area))cv2.imshow("coloredlabels",image)print("totalnumber:",num_labels-1)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-10所示,顯示的目標(biāo)數(shù)目是totalnumber:87(a)原圖像(b)帶有標(biāo)記的圖像圖10-10帶有統(tǒng)計(jì)信息的圖像10.6分水嶺算法

分水嶺算法是把鄰近像素間的相似性作為重要的參考因素,在空間位置上相近和灰度值相近的像素點(diǎn)互相連接起來構(gòu)成的一個封閉區(qū)域。

如果把灰度圖與地形圖作對比,灰度值低的地方是山谷,灰度值高的地方是山峰,這樣山峰包圍了山谷,也天然的形成了分割線。任意的灰度圖像可以被看作是地質(zhì)學(xué)表面,高亮度的地方是山峰,低亮度的地方是山谷。給每個孤立的山谷(局部最小值)標(biāo)記不同顏色的水(標(biāo)簽),當(dāng)水漲起來,根據(jù)周圍的山峰(梯度),不同的山谷也就是不同的顏色會開始合并。要避免合并,可以在水要合并的地方建立障礙,直到所有山峰都被淹沒。所創(chuàng)建的障礙就是分割結(jié)果,這就是分水嶺算法的原理。分水嶺圖像分割算法用到的函數(shù)如下:(1)距離變換函數(shù)cv2.distanceTransform的語法格式是:dst=cv2.distanceTransform(src,distanceType,maskSize)其中輸入輸出參數(shù)為:dst:輸出結(jié)果中包含計(jì)算的距離,這是一個32-bit

float單通道的Mat類型數(shù)組,大小與輸入圖片相同;src:8位單通道(二進(jìn)制)源圖像;distanceType:距離類型:0、1、2分別表示CV_DIST_L1,CV_DIST_L2,CV_DIST_C;maskSize:距離變換蒙版的大小。它可以是3、5或CV_DIST_MASK_PRECISE。OpenCV實(shí)現(xiàn)了一個基于掩模的分水嶺算法,可以指定哪些是要合并的點(diǎn),哪些不是,是一個交互式的圖像分割方法,我們要做的是給不同的區(qū)域標(biāo)記上不同的標(biāo)簽。前景或者目標(biāo)用一種顏色加上標(biāo)簽,背景或者非目標(biāo)加上另一個顏色,最后不知道是什么的區(qū)域標(biāo)記為0,然后使用分水嶺算法。距離變換的基本含義是計(jì)算一個圖像中非零像素點(diǎn)到最近的零像素點(diǎn)的距離,也就是到零像素點(diǎn)的最短距離。根據(jù)各個像素點(diǎn)的距離值,設(shè)置為不同的灰度值,這樣就完成了二值圖像的距離變換。(2)計(jì)算二值圖像的連通域標(biāo)記cv2.connectedComponents函數(shù)語法格式是:num_objects,labels=cv2.connectedComponents(image)其中輸入輸出參數(shù)為:image:也就是輸入圖像,必須是二值圖,即8位單通道圖像;num_objects:所有連通域的數(shù)目;labels:圖像上每一像素的標(biāo)記,用數(shù)字1、2、3…表示(不同的數(shù)字表示不同的連通域)。(3)分水嶺函數(shù)cv2.watershed的語法格式是:dst=cv2.watershed(image,markers)其中輸入輸出參數(shù)為:image:輸入8位3通道的圖像;markers:在執(zhí)行分水嶺函數(shù)watershed之前,必須對第二個參數(shù)markers進(jìn)行處理,它應(yīng)該包含不同區(qū)域的輪廓,每個輪廓有一個自己唯一的編號,輪廓的定位可以通過OpenCV中findContours方法實(shí)現(xiàn),這個是執(zhí)行分水嶺之前的要求。分水嶺函數(shù)中的第二個參數(shù)markers,能夠根據(jù)markers傳入的輪廓編號作為種子(即注水點(diǎn)),

對圖像上其他的像素點(diǎn)根據(jù)分水嶺算法規(guī)則進(jìn)行判斷,并對每個像素點(diǎn)的區(qū)域歸屬進(jìn)行劃定,直到處理完圖像上所有像素點(diǎn)。而區(qū)域與區(qū)域之間分界處的值被置為“-1”,以做區(qū)分。即第二個參數(shù)markers必須包含了種子點(diǎn)信息。圖像灰度化、濾波、Canny邊緣檢測;查找輪廓(findContours),并且把輪廓信息按照不同的編號繪制到watershed的第二個參數(shù)merkers上,相當(dāng)于標(biāo)記注水點(diǎn);分水嶺運(yùn)算。輪廓繪制分割出來的區(qū)域,可以使用隨機(jī)顏色填充,或者跟原始圖像進(jìn)行融合,以得到更好的顯示效果。分水嶺圖像自動分割的實(shí)現(xiàn)步驟:【例10.10】利用OpenCV提供的cv2.watershed()函數(shù)實(shí)現(xiàn)圖像的分水嶺分割圖像。程序代碼如下:importcv2importnumpyasnpimportmatplotlib.pyplotasplt

image=cv2.imread("d:/pics/bi.jpg")gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)imagergb=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)

##二值化ret1,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)##去噪聲kernel=np.ones((3,3),np.uint8)opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)

##確定背景區(qū)域sure_bg=cv2.dilate(opening,kernel,iterations=3)##尋找前景區(qū)域dist_transform=cv2.distanceTransform(opening,cv2.DIST_L2,5)ret2,sure_fg=cv2.threshold(dist_transform,0.005*dist_transform.max(),255,0)##找到未知區(qū)域sure_fg=np.uint8(sure_fg)unknown=cv2.subtract(sure_bg,sure_fg)

##類別標(biāo)記ret3,markers=cv2.connectedComponents(sure_fg)##分水嶺分割img=cv2.watershed(image,markers)

plt.subplot(121),plt.title('originimage')plt.imshow(imagergb)plt.axis('off')plt.subplot(122),plt.title('watershedimage')plt.imshow(img)plt.axis('off')plt.show()程序運(yùn)行結(jié)果如圖10-11所示圖10-11分水嶺算法分割圖像均值漂移算法原理和函數(shù):均值漂移(meanShfit)算法是一種通用的聚類算法,它的基本原理是:對于給定的一定數(shù)量樣本,任選其中一個樣本,以該樣本為中心點(diǎn)劃定一個圓形區(qū)域,求取該圓形區(qū)域內(nèi)樣本的質(zhì)心,即密度最大處的點(diǎn),再以該點(diǎn)為中心繼續(xù)執(zhí)行上述迭代過程,直至最終收斂??梢岳镁灯扑惴ǖ倪@個特性,實(shí)現(xiàn)彩色圖像分割,OpenCV中對應(yīng)的函數(shù)是pyrMeanShiftFiltering。這個函數(shù)嚴(yán)格來說并不是圖像的分割,而是圖像在色彩層面的平滑濾波,它可以中和色彩分布相近的顏色,平滑色彩細(xì)節(jié),侵蝕掉面積較小的顏色區(qū)域,所以在OpenCV中它的后綴是濾波“Filter”,而不是分割“segment”。均值漂移函數(shù)cv2.pyrMeanShiftFilteringde語法格式為:dst=cv2.pyrMeanShiftFiltering(src,sp,sr,maxLevel=1,termcrit)其中輸入輸出參數(shù)如下:dst:輸出圖像,跟輸入src有同樣的大小和數(shù)據(jù)格式;src:輸入圖像,8位,三通道的彩色圖像,并不要求必須是RGB格式,HSV、YUV等OpenCV中的彩色圖像格式均可;sp:定義的漂移物理空間半徑大小;sr:定義的漂移色彩空間半徑大??;maxLevel:定義金字塔的最大層數(shù);termcrit:定義的漂移迭代終止條件,可以設(shè)置為迭代次數(shù)滿足終止,迭代目標(biāo)與中心點(diǎn)偏差滿足終止,或者兩者的結(jié)合?!纠?0.11】自定義分水嶺變換實(shí)現(xiàn)分割圖像。程序代碼如下:importcv2importnumpyasnpdefwatershed(src):#濾波blurred=cv2.pyrMeanShiftFiltering(src,10,100)gray=cv2.cvtColor(blurred,cv2.COLOR_BGR2GRAY)ret,binary=cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)cv2.imshow('binary-image',binary)#形態(tài)學(xué)處理kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))#iterations=2連續(xù)兩次進(jìn)行開操作mb=cv2.morphologyEx(binary,cv2.MORPH_OPEN,kernel,iterations=2)sure_bg=cv2.dilate(mb,kernel,iterations=3)cv2.imshow('mor-opt',sure_bg)#距離變換,掩膜大小是3,cv.DIST_L2是距離的方法dist=cv2.distanceTransform(mb,cv2.DIST_L2,3)dist_output=cv2.normalize(dist,0,1.0,cv2.NORM_MINMAX)cv2.imshow('distance-t',dist_output*50)

ret,surface=cv2.threshold(dist,dist.max()*0.6,255,cv2.THRESH_BINARY)cv2.imshow('surface-bin',surface)#找到未知區(qū)域surface_fg=np.uint8(surface)unknown=cv2.subtract(sure_bg,surface_fg)#類別標(biāo)記ret,markers=cv2.connectedComponents(surface_fg)print('ret=',ret)

#為所有的標(biāo)記加1,保證背景是0而不是1markers=markers+1markers[unknown==255]=0#現(xiàn)在讓所有的未知區(qū)域?yàn)?#分水嶺變換markers=cv2.watershed(src,markers=markers)#標(biāo)記圖像將被修改。邊界區(qū)域?qū)?biāo)記為-1。src[markers==-1]=[0,0,255]cv2.imshow('result',src)if__name__=="__main__":img=cv2.imread('d:/pics/coins2.jpg')dWindow('inputimage',cv2.WINDOW_AUTOSIZE)cv2.imshow('inputimage',img)watershed(img)cv2.waitKey(0)cv2.destroyAllWindows()程序運(yùn)行結(jié)果如圖10-12所示(a)原圖像(b)二值化圖像(c)開運(yùn)算和膨脹運(yùn)算后圖像(d)距離變換后圖像(e)距離變換、二值化后圖像(f)分水嶺變換圖像圖10-12自定義分水嶺變換分割圖像OpenCV中分水嶺算法在OpenCV中,我們需要給不同區(qū)域貼上不同的標(biāo)簽。用大于1的整數(shù)表示我們確定為前景或?qū)ο蟮膮^(qū)域,用1表示我們確定為背景或非對象的區(qū)域,最后用0表示我們無法確定的區(qū)域。然后應(yīng)用分水嶺算法,我們的標(biāo)記圖像將被更新,更新后的標(biāo)記圖像的邊界像素值為-1。下面對相互接觸的硬幣應(yīng)用距離變換和分水嶺分割。/p/677415381.先使用Otsu's二值化對圖像進(jìn)行二值化import

cv2

import

numpy

as

np

img

=

cv2.imread('coins.png')gray

=

cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)ret,thresh

=

cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

先使用開運(yùn)算去除圖像中的細(xì)小白色噪點(diǎn),然后通過腐蝕運(yùn)算移除邊界像素,得到的圖像中的白色區(qū)域肯定是真實(shí)前景,即靠近硬幣中心的區(qū)域(下面左邊的圖);膨脹運(yùn)算使得一部分背景成為了物體到的邊界,得到的圖像中的黑色區(qū)域肯定是真實(shí)背景,即遠(yuǎn)離硬幣的區(qū)域(下面中間的圖)。2.剩下的區(qū)域(硬幣的邊界附近)還不能確定是前景還是背景??赏ㄟ^膨脹圖減去腐蝕圖得到,下圖中的白色部分為不確定區(qū)域(下面右邊的圖)。#noiseremoval

kernel

=

cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))opening

=

cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)sure_bg

=

cv2.dilate(opening,kernel,iterations=2)

#surebackgroundarea

sure_fg

=

cv2.erode(opening,kernel,iterations=2)

#sureforegroundarea

unknown

=

cv2.subtract(sure_bg,sure_fg)

#unknownarea

3.距離轉(zhuǎn)換圖像:剩下的區(qū)域不確定是硬幣還是背景,這些區(qū)域通常在前景和背景接觸的區(qū)域(或者兩個不同硬幣接觸的區(qū)域),我們稱之為邊界。通過分水嶺算法應(yīng)該能找到確定的邊界。

由于硬幣之間彼此接觸,我們使用另一個確定前景的方法,就是帶閾值的距離變換。

下面左邊的圖為得到的距離轉(zhuǎn)換圖像,其中每個像素的值為其到最近的背景像素(灰度值為0)的距離,可以看到硬幣的中心像素值最大(中心離背景像素最遠(yuǎn))。對其進(jìn)行二值處理就得到了分離的前景圖(下面中間的圖),白色區(qū)域肯定是硬幣區(qū)域,而且還相互分離,下面右邊的圖為之前的膨脹圖減去中間這個表示前景的圖。#Performthedistancetransformalgorithm

dist_transform

=

cv2.distanceTransform(opening,cv2.DIST_L2,5)#Normalizethedistanceimageforrange={0.0,1.0}

cv2.normalize(dist_transform,dist_transform,0,1.0,cv2.NORM_MINMAX)#Findingsureforegroundarea

ret,sure_fg

=

cv2.threshold(dist_transform,0.5*dist_transform.max(),255,0)#Findingunknownregion

sure_fg

=

np.uint8(sure_fg)unknown

=

cv2.subtract(sure_bg,sure_fg)

4.創(chuàng)建標(biāo)記:現(xiàn)在我們可以確定哪

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論