1.最大类间方差法:根据直方图以某一会灰度为阈值将图像分割成两部分,计算两组的方差,当被分成的两组之间的方差最大时,这个灰度为阈值灰度值。(基于阈值分割)
img = imread('1.jpg');%原图
I_gray=rgb2gray(img);%转换为灰度图
subplot(121),imshow(img);
%转换为双精度
I_double=double(I_gray);
[wid,len]=size(I_gray);%图像的大小
%灰度级
colorLevel=256;
%直方图
hist=zeros(colorLevel,1);
%计算直方图
for i=1:wid
for j=1:len
m=I_gray(i,j)+1;%图像的灰度级m
hist(m)=hist(m)+1;%灰度值为i的像素和
end
end
%直方图归一化
hist=hist/(wid*len);%各灰度值概率 Pi
miuT=0;%定义总体均值
for m=1:colorLevel
miuT=miuT+(m-1)*hist(m); %总体均值
end
xigmaB2=0;%
for mindex=1:colorLevel
threshold=mindex-1;%设定阈值
omega1=0;%目标概率
omega2=0;%背景概率
for m=1:threshold-1
omega1=omega1+hist(m);% 目标概率 W0
end
omega2=1-omega1; %背景的概率 W1
miu1=0;%目标的平均灰度值
miu2=0;%背景的平均灰度值
for m=1:colorLevel
if m<threshold
miu1=miu1+(m-1)*hist(m);%目标 i*pi的累加值[1 threshold]
else
miu2=miu2+(m-1)*hist(m);%背景 i*pi的累加值[threshold m]
end
end
miu1=miu1/omega1;%目标的平均灰度值
miu2=miu2/omega2;%背景的平均灰度值
xigmaB21=omega1*(miu1-miuT)^2+omega2*(miu2-miuT)^2;%最大方差
xigma(mindex)=xigmaB21;%先设定一个值 再遍历所有灰度级
%找到xigmaB21的值最大
if xigmaB21>xigmaB2
finalT=threshold;%找到阈值 灰度级
xigmaB2=xigmaB21;%方差为最大
end
end
%阈值归一化
fT=finalT/255;
for i=1:wid
for j=1:len
if I_double(i,j)>finalT %大于所设定的均值 则为目标
bin(i,j)=0;
else
bin(i,j)=1;
end
end
end
subplot(122),imshow(bin);
2.局部阈值算法: 主要应用于图像中有阴影,光照不均匀,各处的对比度不同,有突出噪声,背景灰度突变等 (基于阈值)
function bw=adaptivethreshold(IM,ws,C,tm)
% 功能:自适应图像分割
% IM-待分割的原始图像 ws平均滤波时的窗口大小
%C 常量 根据经验选择合适的参数
% tm -开关变量 1=中值滤波 0=均值滤波
%bw- 图像分割后的二值图像
%输入参数处理
if (nargin<3)
error('You must provide the image IM, the window size ws, and C');
elseif(nargin==3)
tm=0;
elseif(tm~=0&&tm~=1)
error('tm must be 0 or 1');
end
IM=mat2gray(IM);
if tm==0
%图像均值滤波
mIM=imfilter(IM,fspecial('average',ws),'replicate');
else
%图像进行中值滤波
mIM=medfilt2(IM,[ws,ws]);
end
sIM=mIM-IM-C;
bw=im2bw(sIM,0);
bw=imcomplement(bw);
3. K-means均值法:是一种广泛的聚类方法
function [mu,mask]=kmeans(ima,k)
%功能·:运用K-means算法对图像进行分割
% 输入 ima-输入的灰度图像 K-分类数
%输出 mu -均值类向量 mask-分类后的图像
ima=double(ima);
copy=ima;
ima=ima(:);
mi=min(ima);%找到最小值
ima=ima-mi+1;
s=length(ima);%有多少灰度级
%计算图像灰度直方图
m=max(ima)+1;%图像最大灰度值
h=zeros(1,m);
hc=zeros(1,m);
for i=1:s
if (ima(i)>0)
h(ima(i))=h(ima(i))+1;%灰度值i累加
end
end
ind =find(h);
h1=length(ind);
%初始化质心
mu=(1:k)*m/(k+1);
%start process
while(true)
oldmu=mu;
%现有的分类·
for i=1:h1
c=abs(ind(i)-mu);
cc=find(c==min(c));
hc(ind(i))=cc(1);
end
%重新计算均值
for i=1:k
a=find(hc==i)
mu(i)=sum(a.*h(a))/sum(h(a));
end
if(mu==oldmu)
break;
end
end
%计算生成分类后的图像
s=size(copy);
mask=zeros(s);
for i=1:s(1)
for j=1:s(2)
c=abs(copy(i,j)-mu);
a=find(c==min(c));
mask(i,j)=a(1);
end
end
mu=mu+mi-1;
4.
基于Otsu的阈值分割方法,函数调用格式:
skimage.filters.threshold_otsu(image, nbins=256)
参数image是指灰度图像,返回一个阈值。
function [mu,mask]=kmeans(ima,k)
%功能·:运用K-means算法对图像进行分割
% 输入 ima-输入的灰度图像 K-分类数
%输出 mu -均值类向量 mask-分类后的图像
ima=double(ima);
copy=ima;
ima=ima(:);
mi=min(ima);%找到最小值
ima=ima-mi+1;
s=length(ima);%有多少灰度级
%计算图像灰度直方图
m=max(ima)+1;%图像最大灰度值
h=zeros(1,m);
hc=zeros(1,m);
for i=1:s
if (ima(i)>0)
h(ima(i))=h(ima(i))+1;%灰度值i累加
end
end
ind =find(h);
h1=length(ind);
%初始化质心
mu=(1:k)*m/(k+1);
%start process
while(true)
oldmu=mu;
%现有的分类·
for i=1:h1
c=abs(ind(i)-mu);
cc=find(c==min(c));
hc(ind(i))=cc(1);
end
%重新计算均值
for i=1:k
a=find(hc==i)
mu(i)=sum(a.*h(a))/sum(h(a));
end
if(mu==oldmu)
break;
end
end
%计算生成分类后的图像
s=size(copy);
mask=zeros(s);
for i=1:s(1)
for j=1:s(2)
c=abs(copy(i,j)-mu);
a=find(c==min(c));
mask(i,j)=a(1);
end
end
mu=mu+mi-1;
5.基于边缘
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
import sys
BLUE = [255,0,0] # rectangle color
RED = [0,0,255] # PR BG
GREEN = [0,255,0] # PR FG
BLACK = [0,0,0] # sure BG
WHITE = [255,255,255] # sure FG
DRAW_BG = {'color' : BLACK, 'val' : 0}
DRAW_FG = {'color' : WHITE, 'val' : 1}
DRAW_PR_FG = {'color' : GREEN, 'val' : 3}
DRAW_PR_BG = {'color' : RED, 'val' : 2}
# setting up flags
rect = (0,0,1,1)
drawing = False # flag for drawing curves
rectangle = False # flag for drawing rect
rect_over = False # flag to check if rect drawn
rect_or_mask = 100 # flag for selecting rect or mask mode
value = DRAW_FG # drawing initialized to FG
thickness = 3 # brush thickness
def onmouse(event,x,y,flags,param):
global img,img2,drawing,value,mask,rectangle,rect,rect_or_mask,ix,iy,rect_over
# Draw Rectangle
if event == cv.EVENT_RBUTTONDOWN:
rectangle = True
ix,iy = x,y
elif event == cv.EVENT_MOUSEMOVE:
if rectangle == True:
img = img2.copy()
cv.rectangle(img,(ix,iy),(x,y),BLUE,2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
rect_or_mask = 0
elif event == cv.EVENT_RBUTTONUP:
rectangle = False
rect_over = True
cv.rectangle(img,(ix,iy),(x,y),BLUE,2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
rect_or_mask = 0
print(" Now press the key 'n' a few times until no further change \n")
# draw touchup curves
if event == cv.EVENT_LBUTTONDOWN:
if rect_over == False:
print("first draw rectangle \n")
else:
drawing = True
cv.circle(img,(x,y),thickness,value['color'],-1)
cv.circle(mask,(x,y),thickness,value['val'],-1)
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
cv.circle(img,(x,y),thickness,value['color'],-1)
cv.circle(mask,(x,y),thickness,value['val'],-1)
elif event == cv.EVENT_LBUTTONUP:
if drawing == True:
drawing = False
cv.circle(img,(x,y),thickness,value['color'],-1)
cv.circle(mask,(x,y),thickness,value['val'],-1)
if __name__ == '__main__':
# print documentation
print(__doc__)
# Loading images
if len(sys.argv) == 2:
filename = sys.argv[1] # for drawing purposes
else:
print("No input image given, so loading default image, ../data/lena.jpg \n")
print("Correct Usage: python grabcut.py <filename> \n")
filename = 'lenna.jpg'
img = cv.imread(filename)
img2 = img.copy() # a copy of original image
mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG
output = np.zeros(img.shape,np.uint8) # output image to be shown
# input and output windows
cv.namedWindow('output')
cv.namedWindow('input')
cv.setMouseCallback('input',onmouse)
cv.moveWindow('input',img.shape[1]+10,90)
print(" Instructions: \n")
print(" Draw a rectangle around the object using right mouse button \n")
while(1):
cv.imshow('output',output)
cv.imshow('input',img)
k = cv.waitKey(1)
# key bindings
if k == 27: # esc to exit
break
elif k == ord('0'): # BG drawing
print(" mark background regions with left mouse button \n")
value = DRAW_BG
elif k == ord('1'): # FG drawing
print(" mark foreground regions with left mouse button \n")
value = DRAW_FG
elif k == ord('2'): # PR_BG drawing
value = DRAW_PR_BG
elif k == ord('3'): # PR_FG drawing
value = DRAW_PR_FG
elif k == ord('s'): # save image
bar = np.zeros((img.shape[0],5,3),np.uint8)
res = np.hstack((img2,bar,img,bar,output))
cv.imwrite('grabcut_output.png',res)
print(" Result saved as image \n")
elif k == ord('r'): # reset everything
print("resetting \n")
rect = (0,0,1,1)
drawing = False
rectangle = False
rect_or_mask = 100
rect_over = False
value = DRAW_FG
img = img2.copy()
mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG
output = np.zeros(img.shape,np.uint8) # output image to be shown
elif k == ord('n'): # segment the image
print(""" For finer touchups, mark foreground and background after pressing keys 0-3
and again press 'n' \n""")
if (rect_or_mask == 0): # grabcut with rect
bgdmodel = np.zeros((1,65),np.float64)
fgdmodel = np.zeros((1,65),np.float64)
cv.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv.GC_INIT_WITH_RECT)
rect_or_mask = 1
elif rect_or_mask == 1: # grabcut with mask
bgdmodel = np.zeros((1,65),np.float64)
fgdmodel = np.zeros((1,65),np.float64)
cv.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv.GC_INIT_WITH_MASK)
mask2 = np.where((mask==1) + (mask==3),255,0).astype('uint8')
output = cv.bitwise_and(img2,img2,mask=mask2)
cv.destroyAllWindows()
6.区域生长法
% Segment based on area, Region Growing;
clear all; close all; clc
[fileName,pathName] = uigetfile('*.*','Please select an image');%文件筐,选择文件
if(fileName)
fileName = strcat(pathName,fileName);
fileName = lower(fileName);%一致的小写字母形式
else
J = 0;%记录区域生长所分割得到的区域
msgbox('Please select an image');
return; %退出程序
end
I = imread(fileName);
if( ~( size(I,3)-3 ))
I = rgb2gray(I);%转化为单通道灰度图
end
I = im2double(I); %图像灰度值归一化到[0,1]之间
Ireshape = imresize(I,[600,800]);
I = Ireshape(51:475,200:699);
gausFilter = fspecial('gaussian',[5 5],0.5);
I = imfilter(I,gausFilter,'replicate');
%种子点的交互式选择
if( exist('x','var') == 0 && exist('y','var') == 0)
subplot(2,2,1),imshow(I,[]);
hold on;
[y,x] = getpts;%鼠标取点 回车确定
x = round(x(1));%选择种子点
y = round(y(1));
end
if( nargin == 0)
reg_maxdist = 0.1;
%nargin是matlab代码编写中常用的一个技巧,主要用于计算当前主函数的输入参数个
%数,一般可以根据nargin的返回值来确定主函数输入参数的缺省值。在实现中,如果
%用户输入的参数个数为零,那么默认为0.2
end
J = zeros(size(I)); % 主函数的返回值,记录区域生长所得到的区域
Isizes = size(I);
reg_mean = I(x,y);%表示分割好的区域内的平均值,初始化为种子点的灰度值
reg_size = 1;%分割的到的区域,初始化只有种子点一个
neg_free = 10000; %动态分配内存的时候每次申请的连续空间大小
neg_list = zeros(neg_free,3);
%定义邻域列表,并且预先分配用于储存待分析的像素点的坐标值和灰度值的空间,加速
%如果图像比较大,需要结合neg_free来实现matlab内存的动态分配
neg_pos = 0;%用于记录neg_list中的待分析的像素点的个数
pixdist = 0;
%记录最新像素点增加到分割区域后的距离测度
%下一次待分析的四个邻域像素点和当前种子点的距离
%如果当前坐标为(x,y)那么通过neigb我们可以得到其四个邻域像素的位置
neigb = [ -1 0;
1 0;
0 -1;
0 1];
%开始进行区域生长,当所有待分析的邻域像素点和已经分割好的区域像素点的灰度值距离
%大于reg_maxdis,区域生长结束
while (pixdist < 0.06 && reg_size < numel(I))
%增加新的邻域像素到neg_list中
for j=1:4
xn = x + neigb(j,1);
yn = y + neigb(j,2);
%检查邻域像素是否超过了图像的边界
ins = (xn>=1)&&(yn>=1)&&(xn<=Isizes(1))&&(yn<=Isizes(1));
%如果邻域像素在图像内部,并且尚未分割好;那么将它添加到邻域列表中
if( ins && J(xn,yn)==0)
neg_pos = neg_pos+1;
neg_list(neg_pos,:) =[ xn, yn, I(xn,yn)];%存储对应点的灰度值
J(xn,yn) = 1;%标注该邻域像素点已经被访问过 并不意味着,他在分割区域内
end
end
%如果分配的内存空问不够,申请新的内存空间
if (neg_pos+10>neg_free)
neg_free = neg_free + 100000;
neg_list((neg_pos +1):neg_free,:) = 0;
end
%从所有待分析的像素点中选择一个像素点,该点的灰度值和已经分割好区域灰度均值的
%差的绝对值时所待分析像素中最小的
dist = abs(neg_list(1:neg_pos,3)-reg_mean);
[pixdist,index] = min(dist);
%计算区域的新的均值
reg_mean = (reg_mean * reg_size +neg_list(index,3))/(reg_size + 1);
reg_size = reg_size + 1;
%将旧的种子点标记为已经分割好的区域像素点
J(x,y)=2;%标志该像素点已经是分割好的像素点
x = neg_list(index,1);
y = neg_list(index,2);
% pause(0.0005);%动态绘制
% if(J(x,y)==2)
% plot(x,y,'r.');
% end
%将新的种子点从待分析的邻域像素列表中移除
neg_list(index,:) = neg_list(neg_pos,:);
neg_pos = neg_pos -1;
end
J = (J==2);%我们之前将分割好的像素点标记为2
hold off;
subplot(2,2,2),imshow(J);
J = bwmorph(J,'dilate');%补充空洞
subplot(2,2,3),imshow(J);
subplot(2,2,4),imshow(I+J);