基于遗传算法的Ostu法在图像分割中的应用
像素关系
相邻像素
位于坐标(x,y)处的像素P有4个水平和垂直的相邻像素,其坐标为:
(x+1,y),(x-1,y),(x,y+1),(x,y-1)
这组相邻元素称为P的4邻域。用N4(P)表示。类似于十字形。
P的4个对角相邻像素坐标如下:
(x+1,y+1),(x+1,y-1),(x-1,y+1),(x-1,y+1)
用ND(P)表示。这些点与N4(P)的4个点一起称为P的8邻域,用N8(P)表示。
邻接,连通,区域和边界
V是定义邻接性的灰度值集合。在二值图像中,如果把具有1值的像素归诸于邻接像素,则V={1}。共定义三种形式的邻接:
A.4邻接。如果q在集合N4(p)中,则具有V中数值的两个像素p和q是4邻接的。
B.8邻接。如果q在集合N8(p)中,则具有V中数值的两个像素p和q是8邻接的。
C.m邻接(混合邻接)。如果q在集合N4(p)中,或q在ND(p)中,且集合N4(p)∩N4(q)中没有来自V中数值的像素,则具有V中数值的两个像素p和q是m邻接的。(m邻接的引进是为了消除8邻接时产生的二义性)
从坐标为(x,y)的p像素点到坐标为(s,t)的q像素点的通路是特定的像素序列,其坐标序列为(x0,y0),(x1,y1),(x2,y2),...,(xn,yn)。其中(x0,y0)为p像素坐标,(xn,yn)为q像素坐标,且(xi-1,yi-1)和(xi,yi)是邻接的。根据邻接的类型,可以定义为4通路,8通路,m通路。
令S是图像中的一个像素子集。对于S中的任何像素p,S中连通到该像素的像素集称为S的连通分量。如果S仅有一个连通分量,则称S为连通集。
分割简介
假设一副图像可以被分割K个非空的子集R1,R2,...,Rn。其中这些子集满足以下条件:
灰度图像一般是根据边界或区域两种情况进行分割,当区域之间的差别很大时,区域间的边界就很明显,这时就可以根据边界进行分割,其优点在于:边缘定位准确,运算速度快;其局限性在于:边缘的连续性和封闭性难以保证,对于复杂图像分割效果较差,出现如:边缘模糊、边缘丢失等现象。边缘检测方法常常依赖于边缘检测算子,从而找到图像边缘;常用的检测算子有: Roberts(精度高、对噪声敏感)、 Sobel(对噪声具有一定平滑,但精度低)、 Prewitt、 Canny(检测阶跃型边缘效果好,抗噪强)、 Laplacian 和 Marr 算子(即 LOG 算子,最早由 marr 等人提出,算法简单,速度快、但对噪声敏感)。若边界不是很明显时,就可以根据同一区域内的像素具有相似性这一特征进行分割,常见的区域分割方法主要有: 区域生长法、分裂合并法和分水岭分割方法。
Ostu基本方法
本文使用的Ostu方法是基于全局阈值处理的方法。该方法在类间方差最大的情况下是最佳的。其基本概念是好的阈值分类就它的像素灰度值而论应该是截然不同的,反过来说,就它的灰度值而言给出最好的类间分离的阈值就是好最好的阈值。除此之外,Ostu方法还有一个重要的特性,即它完全以在一副图像的直方图上执行计算为基础,直方图是很容易得到的一维阵列。
图像归一化的直方图具有分量pi=ni/MN,ni表示灰度级为i的像素数。由此整幅图像的平均灰度为
现若假定一个阈值K,由K把输入图像阈值化为两类C1和C2,C1由图像中灰度值在范围[0,k]内的所有像素组成,C2由灰度值在范围[k+1,L-1]的所有像素组成。则像素被分类到C1的概率为
直至级k的累积像素灰度均值为
类间方差由此给出
从而最佳阈值为K,就是其最大化类间反差。
遗传算法
关于遗传算法部分,可参照前面写过的博文 《遗传算法详解》 。
遗传算法Ostu的应用
在编码方式上面选择由8位二进制表示像素灰度值,同时使用类间方差作为适应度函数,对于选择算子则使用轮盘赌的方法。
下面是算法的基本实现代码
# -*- coding: cp936 -*-
import numpy as np
import cv2
import random
#将不足8位的染色体扩展为8位
def expand(k):
for i in range(len(k)):
k[i]=k[i][0:2]+'0'*(10-len(k))+k[i][2:len(k)]
return k
def Hist(image):
a=[0]*256 #乘256相当于<<8,即左移8位
h=image.shape[0] #打印数组各个维度的长度
w=image.shape[1]
MN=h*w
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)
#适应度算子 Ostu全局算法
def fitness(k,hist,averge):
Var=[0]*len(k) #左移len(k)位
Varg=0.0
for i in range(256):
Varg=Varg+pow((i-average),2)*hist[i] #pow(x,y)表示x的y次幂,pow(x,y,z)表示x的y次幂之后再除以z的余数
for j in range(len(k)):
P1=0.0
m=0.0
for i in range(int(k[j],2)+1):
P1=P1+hist[i]
m=m+hist[i]*i
Var[j]=pow(average*P1-m,2)/(P1*(1-P1)*Varg+0.00001)
return Var
#选择算子 轮盘赌选择算法
def wheel_selection(k,Var):
var=[0.0]*len(Var)
s=0.0
n=['']*len(k)
for i in range(len(Var)):
var[i]=Var[i]/sum(Var)
for i in range(1,len(Var)):
var[i]=var[i]+var[i-1]
for i in range(len(k)):
s=random.random()
for j in range(len(var)):
if s<=var[j]:
n[i]=k[j]
return n
#变异算子
def Variation(Next):
for i in range(len(Next)):
if random.random()<0.06:
Next[i]=bin(int(Next[i],2)+2)
return Next
#交叉算子
def Cross(Next):
for i in range(len(Next)-1):
if random.random()<0.6:
Next[i]=Next[i][:5]+Next[i+1][5:8]+Next[i][8:10]
return Next
#im为读取图像的数据
#seed表示每一代将要进行选择,复制,变异和交叉的个体
#last_fit和now_fit分别表示父子两代群体的最大适应度
#k为在规定代数结束后,最大适应度个体的染色体表示,即为最佳阈值
a=r'D:\Python27\0.jpg'
im=cv2.imread(a,0)
items=range(0,min(im.shape))
random.shuffle(items)
x=items[0:20]
y=items[21:41]
seed=[]
last_fit=0.0
now_fit=0.0
Var=0.0
times=0
k=0
hist,average=Hist(im)
for i in range(0,20):
seed.append(bin(im[x[i]][y[i]]))
while times<20:
Var=fitness(seed,hist,average)
Next=wheel_selection(seed,Var)
Next=Cross(Next)
Next=expand(Variation(Next))
seed=Next
#Var=fitness(seed,hist,average)
last_fit=now_fit
now_fit=max(Var)
times=times+1
print max(Var)
for j in range(len(Var)):
if Var[j]==max(Var):
k=int(Next[j],2)
print k
下图为阈值分割后的效果图,在程序多次运行中,发现结果有时会出现随机游走的情况,这个可以通过改变交叉概率和变异概率等参数进行一定程度地改善。
参考资料:
[1] 图像分割方法及性能评价综述,丁亮,张永平,张雪英,中国科技论文在线
[2] 数字图像处理,冈萨雷斯
---------------------
作者:sam-X
来源:CSDN
原文:https://blog.csdn.net/u010945683/article/details/41780491
版权声明:本文为博主原创文章,转载请附上博文链接!