《OpenCV算法精解——基于Python与C++》第六章阈值分割
生活随笔
收集整理的這篇文章主要介紹了
《OpenCV算法精解——基于Python与C++》第六章阈值分割
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
第六章代碼總結(jié):
Github地址
本章通過選取閾值點,對圖像主體與背景進(jìn)行分割。分為手動與自動。
自動:直方圖技術(shù)法,熵算法(太麻煩了),Otsu(全局法中最棒),自適應(yīng)閾值法(無敵)
- 手動閾值
- 自動閾值 效果Otsu > Triangle > histogram = 熵
- 自適應(yīng)閾值法:
最后6.6介紹二值圖的邏輯運算,要求圖像格式相同
- dst_and = cv2.bitwise_and(src1,src2)#與運算
- dst_or = cv2.bitwise_or(src1,src2)#或運算
6.1_閾值分割
閾值分割(cv2.threshold)又稱二值化處理,通過選取閾值分割,對圖像分割成不同的像素集合。對前景與背景有較強對比度的特別有效。
threshold(src,dst,thresh,maxval,type)
thresh:閾值;maxVal:輸出的最大灰度,一般為255;type:THRESH_BINARY = 0 , 大于閾值輸出maxVal,小于等于輸出0;THRESH_BINARY_INV =1 相反THRESH_OTSU = 8 ,自動計算閾值,并默認(rèn)搭配THRESH_BINARY輸出。THRESH_TRIANGLE =16 ,同OTSUPython API:
#-*- coding:utf-8 -*- import numpy as np import cv2 src = np.array([[123,234,68],[33,51,17],[48,98,234],[129,89,27],[45,167,134]],np.uint8)#手動設(shè)置閾值 thresh = 150 maxVal = 255 dst = cv2.threshold(src,thresh,maxVal,cv2.THRESH_BINARY) print dst #otsu自動閾值處理 otsu_thresh = 0 otsu_thresh,dst_otsu = cv2.threshold(src,otsu_thresh,255,8)#最后的參數(shù)8代表cv2.THRESH_OTSU print otsu_thresh,dst_otsu#TRIANGLE處理 triangle_thresh = 0 triangle_thresh,dst_tri = cv2.threshold(src,triangle_thresh,255,16) print triangle_thresh,dst_tri (150.0, array([[ 0, 255, 0],[ 0, 0, 0],[ 0, 0, 255],[ 0, 0, 0],[ 0, 255, 0]], dtype=uint8)) 98.0 [[255 255 0][ 0 0 0][ 0 0 255][255 0 0][ 0 255 255]] 232.0 [[ 0 255 0][ 0 0 0][ 0 0 255][ 0 0 0][ 0 0 0]]原理:
import numpy as np src = np.array([[123,234,68],[33,51,17],[48,98,234],[129,89,27],[45,167,134]]) #大于閾值的像素點輸出255,小于等于閾值輸出0 src[src>150]=255 src[src<=150]=0 src array([[ 0, 255, 0],[ 0, 0, 0],[ 0, 0, 255],[ 0, 0, 0],[ 0, 255, 0]])6.2_直方圖技術(shù)法
- 0:黑色,255:白色,理解為亮度值;
- 直方圖技術(shù)法通過選出直方圖中的兩峰頂間的最小峰谷作為閾值。
- 方法同THRESH_TRIANGLE:三角法是將最高峰頂置于圖像亮側(cè),連接原點與峰頂,選擇直方圖距離這條連線的最遠(yuǎn)點就為峰谷,及閾值。
原理:
import numpy as npdef calcGrayHist(image):#灰度圖像矩陣的高,寬rows,cols = image.shape#存儲灰度直方圖grayHist = np.zeros([256],np.uint64)for r in xrange(rows):for c in xrange(cols):grayHist[image[r][c]] +=1 #這里應(yīng)該指的是,image[r][c]對應(yīng)灰度數(shù)值,在grayHist上+1return grayHistdef threshTwoPeaks(image):#計算灰度直方圖histogram = calcGrayHist(image)#找最大峰頂maxLoc = np.where(histogram==np.max(histogram))firstPeak = maxLoc[0][0]#尋找第二個峰值對應(yīng)的灰度值measureDists = np.zeros([256],np.float32)for k in xrange(256):measureDists[k] = pow(k-firstPeak,2)*histogram[k]maxLoc2 = np.where(measureDists==np.max(measureDists))secondPeak = maxLoc2[0][0]#找到兩個峰值間的最小值對應(yīng)的灰度值thresh = 0 #先給thresh賦值,不然無定義if firstPeak > secondPeak:temp = histogram[int(secondPeak):int(firstPeak)]minLoc = np.where(temp == np.min(temp))thresh = secondPeak + minLoc[0][0] + 1else:temp = histogram[int(firstPeak):int(secondPeak)]minLoc = np.where(np.min(temp))thresh = firstPeak + minLoc[0][0] + 1#找到閾值后,對圖像進(jìn)行閾值化處理threshImage_out = image.copy()#把src拷貝一份threshImage_out[threshImage_out>thresh]=255threshImage_out[threshImage_out<=thresh]=0return (thresh,threshImage_out) import cv2 import numpy as np import matplotlib.pyplot as pltsrc = cv2.imread("img7.jpg",0)#otsu the_otsu = 0 the_otsu,dst_otsu = cv2.threshold(src,the_otsu,255,8)#triangle the_tra = 0 the_tra,dst_tra = cv2.threshold(src,the_tra,255,16)#histogram the_hist = 0 the_hist,dst_hist = threshTwoPeaks(src)titles = ["src","otsu","tra","histogram"] images = [src,dst_otsu,dst_tra,dst_hist] for i in range(4):plt.subplot(1,4,i+1)plt.title(titles[i])plt.imshow(images[i]) plt.show()
講道理,otsu自動計算的更好
6.3_熵算法
- 熵算法使用信息論的概念,將圖像看做信源,來計算灰度級熵,一頓操作猛如虎計算出閾值,我反正沒看懂。
- 結(jié)果與histogram相近
效果與histogram直方圖法差不多,原理太復(fù)雜
6.4_Otsu
終于要介紹Otsu算法了,Otsu算法是最大方差法。在前面已經(jīng)使用過了
- otsu_thresh,dst_otsu =
cv2.threshold(src,otsu_thresh,255,8)#最后的參數(shù)8代表cv2.THRESH_OTSU - 只支持8位圖
原理:
def calcGrayHist(image):rows,cols = image.shapegrayHist = np.zeros([256],np.unit64)for r in xrange(rows):for c in xrange(cols):grayHist[image[r][c]] += 1return grayHistdef otsu(image):rows,cols =image.shape#計算灰度直方圖histogram = calcGrayHist(image)#歸一化uniformGrayHist = grayHist/float(rows*cols)#計算零階累計矩和一階累積矩zeroCumuMoment = np.zeros([256],np.float32)oneCumuMoment = np.zeros([256],np.float32)for k in xrange(256):if k == 0:zeroCumuMoment[k] = uniformGrayHist[0]oneCumuMoment[k] = (k)*uniformGrayHist[0]else:zeroCumuMoment[k] = zeroCumuMoment[k-1] + uniformGrayHist[k]oneCumuMoment[k] = oneCumuMoment[k-1] + k*uniformGrayHist[k]#計算類間方差variance = np.zeros([256],np.float32)for k in xrange(255):if zeroCumuMoment(k) == 0 or zeroCumuMoment[k] ==1:variance[k] = 0else:variance[k] = math.pow(oneCumuMonet[255]*zeroCumuMoment[k] - oneCumuMoment[k],2)/(zeroCumuMoment[k]*(1.0-zeroCumuMoment[k]))#找到閾值threshLoc = np.where(variance[0:255] == np.max(variance[0:255]))thresh = threshLoc[0][0]#閾值處理threshold = np.copy(image)threshold[threshold > thresh] = 255threshold[threshold <=thresh] = 0return (threshold,thresh)Otsu核心在計算最大類間方差,找到其所在像素值也就對應(yīng)了閾值。效果相當(dāng)可以。
6.5_自適應(yīng)閾值
- 全局閾值分割以O(shè)tsu算法為首,表現(xiàn)出了很好的分割效果。缺點是對光照不均的圖面,全局閾值分割無法完全分割出主體。
- 自適應(yīng)閾值算法針對每一個位置的灰度值設(shè)置相對應(yīng)的閾值。通過對圖像進(jìn)行平滑處理(均值(mean),高斯(Gaussian),中值(median)),計算像素鄰域的灰度均值,乘以比例系數(shù)(1-ratio),ratio一般取0.15,結(jié)果作為該像素的閾值參考值。
- 平滑算子寬度決定分割出的物體尺寸,書本經(jīng)驗提出,平滑算子寬度必須大于被識別物體的寬度。當(dāng)然,高寬為基數(shù)(平滑處理要求)
原理:
import cv2 import numpy as np import matplotlib.pyplot as plt #平滑處理采用均值平滑(cv2.blur或者cv2.boxFilter) def adaptiveThresh(I,winSize,ratio=0.15):#比例系數(shù)ratio一般0.15#第一步:圖像平滑I_mean = cv2.blur(I,winSize)#I_mean = cv2.boxFilter(I,cv2.CV_32FC1,winSize)#結(jié)果相同,但不知道blur沒有指定32位有沒有影響,等下試試#第二步:原圖與平滑結(jié)果做差out = I - (1.0-ratio)*I_mean#第三步: 當(dāng)差值大于或等于0,輸出255,;小于0,輸出0out[out>=0] = 255out[out<0] = 0out = out.astype(np.uint8)#圖像格式轉(zhuǎn)換為255,return outsrc = cv2.imread("image3.png",0) dst = adaptiveThresh(src,(7,7),0.15) plt.subplot(1,2,1) plt.imshow(src) plt.subplot(1,2,2) plt.imshow(dst) plt.show- PythonAPI:cv2.adaptiveThreshold(src,dst,maxValue,adaptiveMethod,thresholdType,blockSize,c)
- maxValue:最大輸出灰度值
- adaptiveMethod:
ADAPTIVE_THRESH_MEAN_C:均值平滑
ADAPTIVE_THRESH_GAUSSIAN_C:高斯平滑
ADAPTIVE_THRESH_MEDIAN_C:中值平滑 - thresholdType:THRESH_BINARY、THRESH_BINARY_INV
- blockSIZE:滑動窗口大小,原理里的winSize
- C:比例算子,原理里的ratio
通過adaptiveThresh進(jìn)行閾值分割效果比OpencvAPI好一些,查找資料,嘗試對src先進(jìn)行平滑處理,再自適應(yīng)閾值分割
重新編寫的代碼如下
import cv2 import matplotlib.pyplot as pltsrc = cv2.imread("image3.png",0)#Otsu算法 the_Otsu = 0 the_Otsu,dst_Otsu = cv2.threshold(src,the_Otsu,255,8)#用上面定義的adaptiveThresh進(jìn)行閾值分割 dst_adaptiveThresh = adaptiveThresh(src,(7,7),0.15)#7*7的自適應(yīng)算法,PythonAPI src_mean = cv2.blur(src,(7,7)) dst_adaptive_mean = cv2.adaptiveThreshold(src_mean,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,7,0.15) src_gaussian = cv2.GaussianBlur(src,(7,7),5) dst_adaptive_gaussian = cv2.adaptiveThreshold(src_gaussian,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,7,0.15)titles = ("src","Otsu","adp","adp_mean","adp_gaussian") images = (src,dst_Otsu,dst_adaptiveThresh,dst_adaptive_mean,dst_adaptive_gaussian)for i in xrange(5):plt.subplot(2,3,i+1)plt.title(titles[i])plt.imshow(images[i])cv2.imshow(titles[i],images[i]) plt.show() cv2.waitKey(0) cv2.destroyAllWindows()
效果稍微好一些,噪點去除了,但是邊框模糊了,還是沒有代碼實現(xiàn)的好。
6.6_二值圖的邏輯運算
cv2.bitwise_and & cv2.bitwise_or 為與或運算
#-*-coding:utf-8 -*- import cv2 import numpy as np src1 = np.array([[255,0,255]]) src2 = np.array([[255,0,0]])#與運算 dst_and = cv2.bitwise_and(src1,src2) #或運算 dst_or = cv2.bitwise_or(src1,src2) print dst_and print dst_or [[255 0 0]] [[255 0 255]]總結(jié)
以上是生活随笔為你收集整理的《OpenCV算法精解——基于Python与C++》第六章阈值分割的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hdu 4391 Paint The
- 下一篇: 算法补充 2011-9-12