otsu阈值分割
一、OTSU法(大津閾值分割法)介紹
??OTSU算法是由日本學(xué)者OTSU于1979年提出的一種對(duì)圖像進(jìn)行二值化的高效算法,是一種自適應(yīng)的閾值確定的方法,又稱大津閾值分割法,是最小二乘法意義下的最優(yōu)分割。
二、單閾值OTSU法
??設(shè)圖像包含L個(gè)灰度級(jí),灰度值為i的像素點(diǎn)個(gè)數(shù)為Ni,像素總點(diǎn)數(shù)為:
下面給出python源代碼。
#coding:utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt
image = cv2.imread(“E:/python/cv/OTSU/test.jpg”)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
plt.subplot(131), plt.imshow(image, “gray”)
plt.title(“source image”), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.hist(image.ravel(), 256)
plt.title(“Histogram”), plt.xticks([]), plt.yticks([])
ret1, th1 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU) #方法選擇為T(mén)HRESH_OTSU
plt.subplot(133), plt.imshow(th1, “gray”)
plt.title("OTSU,threshold is " + str(ret1)), plt.xticks([]), plt.yticks([])
plt.show()
??結(jié)果如下所示。可以看到,使用OTSU法計(jì)算出來(lái)的閾值為165。
三、多閾值OTSU法
??將單閾值的OTSU推廣到多閾值的圖像分割中,假設(shè)將圖像直方圖分為m+1類,對(duì)應(yīng)的閾值為T(mén)1,T2,···,Tm。則最大類間方差為:
??為求得最優(yōu)閾值,需要使用窮舉搜索,隨著m增大,計(jì)算量驟增。若使用牛頓迭代等優(yōu)化搜索方法,容易陷入局部最優(yōu)解。
四、遺傳算法解OTSU
??遺傳算法是一種基于自然選擇和群體遺傳機(jī)理的搜索算法。它模擬了自然選擇和自然遺傳過(guò)程中發(fā)生的繁殖、交配和突變現(xiàn)象,將每一個(gè)可能的解看作是群體的一個(gè)個(gè)體,并將每個(gè)個(gè)體編碼,根據(jù)設(shè)定的目標(biāo)函數(shù)對(duì)每個(gè)個(gè)體進(jìn)行評(píng)價(jià),給出一個(gè)適應(yīng)度值。開(kāi)始時(shí)隨機(jī)產(chǎn)生一些個(gè)體,利用遺傳算子產(chǎn)生新一代的個(gè)體,新個(gè)體繼承上一代的優(yōu)良性狀,逐步向更優(yōu)解進(jìn)化。由于遺傳算法在每一代同時(shí)搜索參數(shù)空間的不同區(qū)域,從而能夠使找到全局最優(yōu)解的可能性大大增加。遺傳算法屬于啟發(fā)式算法,無(wú)限趨緊最優(yōu)解并收斂。
??那么怎么將圖像分割問(wèn)題抽象成遺傳問(wèn)題,即怎么將問(wèn)題編碼成基因串,如何構(gòu)造適應(yīng)度函數(shù)來(lái)度量每條基因的適應(yīng)度值。假設(shè)如上述三所示,將圖像分為m+1類,則m個(gè)閾值按順序排列起來(lái)構(gòu)成一個(gè)基因串:
由于灰度為0~255,所以可以使用8位二進(jìn)制代碼表示每個(gè)閾值,此時(shí)每個(gè)基因串由長(zhǎng)度為8*m個(gè)比特位的傳組成。
??將類間方差作為其適應(yīng)度函數(shù),類間方差越大,適應(yīng)度函數(shù)值就越高。
??python代碼如下所示:
#coding:utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt
import random
#將不足8m位的染色體擴(kuò)展為8m位
def expand(k, m):
for i in range(len(k)):
k[i] = k[i][:2] + ‘0’*(8*m+2 - len(k[i])) + k[i][2:len(k[i])]
return k
def Hist(image):
a=[0]256
h=image.shape[0]
w=image.shape[1]
MN=hw
average=0.0
for i in range(w):
for j in range(h):
pixel=int(image[j][i])
a[pixel]=a[pixel]+1
for i in range(256):
a[i]=a[i]/float(MN)
average=average+a[i]*i
return a, average
#解析多閾值基因串
def getTh(seed, m):
th = [0, 256]
seedInt = long(seed, 2)
for i in range(0, m):
tmp = seedInt & 255
if tmp != 0:
th.append(tmp)
seedInt = seedInt >> 8
th.sort()
return th
#適應(yīng)度函數(shù) Ostu全局算法
def fitness(seed, p, average, m):
Var = [0.0] * len(seed)
g_muT = 0.0
#選擇算子 輪盤(pán)賭選擇算法
def wheel_selection(seed, Var):
var = [0.0]*len(Var)
s = 0.0
n = [’’]*len(seed)
sumV = sum(Var)
for i in range(len(Var)):
var[i] = Var[i]/sumV
for i in range(1, len(Var)):
var[i] = var[i] + var[i-1]
for i in range(len(seed)):
s = random.random()
for j in range(len(var)):
if s <= var[j]:
n[i] = seed[j]
return n
#單點(diǎn)交叉算子
def Cross(Next, m):
for i in range(0, len(Next) - 1, 2):
if random.random() < 0.7:
if m > 2:
tmp = Next[i][10:]
Next[i] = Next[i][:10] + Next[i+1][10:]
Next[i+1] = Next[i+1][:10] + tmp
else:
tmp = Next[i][6:]
Next[i] = Next[i][:6] + Next[i+1][6:]
Next[i+1] = Next[i+1][:6] + tmp
return Next
#變異算子
def Variation(Next):
for i in range(len(Next)):
if random.random()<0.06:
Next[i]=bin(long(Next[i],2)+2)
return Next
#多閾值分割
def genetic_thres(image, k, m):
th = image
for i in range(image.shape[0]):
for j in range(image.shape[1]):
for t in range(1, len(k)-1):
if k[t-1] <= image[i][j] < k[t]:
th[i][j] = int(k[t-1])
return th
# main
imagesrc = cv2.imread(“E:/python/cv/OTSU/test2.jpg”)
gray = cv2.cvtColor(imagesrc, cv2.COLOR_BGR2GRAY)
m = 3 #閾值數(shù)
items_x = range(0, imagesrc.shape[0])
items_y = range(0, imagesrc.shape[1])
random.shuffle(items_x)
random.shuffle(items_y)
x = items_x[0:20m] #產(chǎn)生隨機(jī)x坐標(biāo)
y = items_y[0:20m] #產(chǎn)生隨機(jī)y坐標(biāo)
seed = []
Var = 0.0
times = 0
k = 0
P, average = Hist(gray) #計(jì)算直方圖,P為各灰度的概率的數(shù)組,average為均值
for i in range(0, 20):
code = long(0)
for j in range(0, m):
code = code + gray[x[ij]][y[ij]] << j*8 #將閾值連起來(lái)組成一個(gè)8*m比特的基因串
seed.append(bin(code)) #生成第一代
while times < 2000:
Var = fitness(seed, P, average, m)
Next = wheel_selection(seed, Var)
Next = Cross(Next, m)
Next = expand(Variation(Next), m)
seed = Next
times = times + 1
for j in range(len(Var)):
if Var[j] == max(Var):
k = getTh(Next[j], m)
print k
plt.subplot(131), plt.imshow(imagesrc, “gray”)
plt.title(“source image”), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.hist(imagesrc.ravel(), 256)
plt.title(“Histogram”), plt.xticks([]), plt.yticks([])
th1 = genetic_thres(gray, k, m)
plt.subplot(133), plt.imshow(th1, “gray”)
titleName = ‘’
for i in range(1, len(k)-1):
titleName = titleName + str(k[i]) + ', ’
titleName = titleName[:len(titleName)-2]
plt.title("threshold is " + titleName), plt.xticks([]), plt.yticks([])
plt.show()
這里使用的是標(biāo)準(zhǔn)二進(jìn)制,若使用格雷碼,應(yīng)該能收斂得更好。結(jié)果如下所示:
總結(jié)
- 上一篇: 一个非常好的各种算法动画图演示网站
- 下一篇: rs485数据线接反_终于有人把RS48