1. im2bw 和 imbinarize 的区别
将图片转换为二值图有两个函数,分别为:
bw = imbinarize(g);
与
bw = im2bw(g);
在 matlab2018 中建议用 imbinarize
来将图片转换为二值图,其参数必须为灰度图。
在 matlab2016 中,只有 im2bw
函数,其参数可以是灰度图或 RGB 图。
从上面两句话可以看出二者的主要区别,两者接收的参数类型不同:
- 在 matlab2016 中可以用
im2bw
直接将 RGB 图像转换为二值图 - 在 matlab2018 中只能先将 RGB 图片转换为灰度图(用
rgb2gray()
函数),再用imbinarize
转换为二值图。
两个函数都是将图片转换为二值图,除了参数的类型不同,两个函数都可再传入一个 [0,1] 的阈值 level
,即:
bw = im2bw(g, level);
或
bw = imbinarize(g, level);
阈值 level
的作用为:
- 用值 1 (白色)替换输入图像中亮度大于
level
比例的所有像素 - 用值 0 (黑色)替换所有其他像素。
当 im2bw
的 level
省略时,默认为 0.5 。
imbinzrize
的阈值默认值我没找到(好像是用某种算法算出的,知道的可以在评论区交流一下),但是可以自己来指定,当指定为 0.5 时候 imbinarize(g, 0.5)
和 im2bw(g)
的效果是一样的。
在不为 imbinarize
指定阈值和指定时的比较见下图:
代码如下:
clear,clc,close all;
f=imread('eye.jpg');
g=rgb2gray(f);
bw1=imbinarize(g);
bw2=imbinarize(g,0.5);
subplot(1,2,1),imshow(bw1),title('默认阈值');
subplot(1,2,2),imshow(bw2),title('指定阈值');
2. 图像分割的综合应用
目的:截取图片中的眼睛
步骤:
-
先转换为二值图,因为单纯的处理二值图更为简便
为了使图片转换为二值图,使用
imbinarize
函数,其中的参数必须为二维的灰度图:根据比较,为了分割出人物的眼睛,可利用
im2bw(g)
或imbinarize(g, 0.5)
转换为二值图,由于是使用matlab2018版本,这里选择使用imbinarize(g, 0.5)
该步代码和效果为:clear,clc,close all; f=imread('eye.jpg'); g=rgb2gray(f); bw=imbinarize(g,0.5); imshow(bw);
-
对二值图bw取反
因为黑色对应的值是 0,白色对应的是 1,因此用取反,将存在的眼睛鼻子嘴巴转换为白色,以方便后面使用
find
函数查找,对图片矩阵值取反用函数:bw = ~bw;
得到该步代码和结果:
clear,clc,close all; f=imread('eye.jpg'); g=rgb2gray(f); bw=imbinarize(g,0.5); bw=~bw; %对二值图bw取反 imshow(bw);
-
通过观察和
sum
函数确定眼睛区域根据
sum
函数的投影原理,由于单纯的横轴方向不利于确定眼睛的坐标位置,因此可以从纵向考虑,先利用纵轴的上下坐标,将眼睛的部分先切割出来,然后去除左右两边的黑色。所以,首要任务是找到眼睛的上下边界。利用
sum
和plot
查看水平投影的值:clear,clc,close all; f=imread('eye.jpg'); g=rgb2gray(f); bw=imbinarize(g,0.5); bw=~bw; %对二值图bw取反 imshow(bw); rs=sum((bw')); %对转置后的图片求sum,以配合plot找出眼睛部分 plot(rs); %输出 rs 数组的值的分布
从图中可以发现,水平投影中有四部分为有值部分,观察得知,第二部分是眼睛所在位置,眼睛部分也是最多的部分,为了让程序更有健壮性,我们可以选取跨度最多的部分的左右坐标,以确定眼睛的上下边界,这里可以新建一个空数组,然后找到每部分的起始坐标和结束坐标,存入数组,通过将每部分两坐标的差值进行比较,找到跨度最大的部分,选其起始和结束坐标作为眼睛的上下边界。新增代码为:
[h,w]=size(bw); %利用size函数可以返回图像的高和宽的值,方便下面控制循环结束 s=[]; %新建空数组,以存储找到的每部分的边界值 a=1;b=1; while(a<h) %控制扫描所有的点 while(rs(a)==0&&a<h) %找到非0点的开始 a=a+1; end b=a; while(rs(b)>0&&b<h) %找到非0点的结束 b=b+1; end s=[s;a b]; %将开始和结束点加入空数组 a=b; end
[h,w]=size(bw); % size 函数可以返回图像的高和宽,高的值存入 h 中,宽的值存入 w 中。
s=[s;a b]; % 给数组 s 新加值,新值为 a 和 b ,因为 (a b) 构成了一个元素,因此可以知道 s 是一个有两列值的数组。
运行过之后可以从右侧工作区发现每个变量的值:
双击工作区s
查看s
的值:
可以发现s
中存的就是每部分的开始和结束坐标,最后多出了一项相同的,是循环到终点满足while
循环的条件导致,可以在while
循环中添加判断语句以控制不出现该项,因为此处不影响,后面用到再添加该段代码。 -
找出差值最大部分的坐标
利用数组
s
的第二列减去第一列,找到差值最大的行,该行的两个值就是眼睛的上下边界新加代码为:
slength = s(:, 2) - s(:, 1); [length, num] = max(slength);
slength = s(:, 2) - s(:, 1); 截取
s
的第二列和第一列并相减,将减后的数组赋给slength
。前面学过,用()
可以进行矩阵的切割,利用括号内的参数,可以选择某几行和某几列构成新的矩阵,该句代码用的也是该思想,用括号内的第一个参数:
选择所有的行,第二个参数单独一个数字选择第 n 列,便返回一个一维数组,两个一维数组相减,就是对应位置的元素值相减,就可以得到每一部分的长度。[length, num] = max(slength); 对数组
slength
使用函数max
,length
为slength
中的最大值,num
为最大值对应的位置。运行后的值为:
-
根据坐标切割眼睛
将眼睛的上下界找到后,用 find函数找出左右边界,切割即得出眼睛位置
hresult=bw(s(num,1):s(num,2),:); %从眼睛的上下边界截取二值图部分,得到中间的眼睛部分 [~,c]=find(hresult); %从中间的眼睛部分找到眼睛的左右边界, ~代表不要该维度的值 result1=bw(s(num,1):s(num,2),min(c):max(c)); %对二值图进行切割 result2=f(s(num,1):s(num,2),min(c):max(c),:); %对原图进行切割 figure,imshow(result1),title('切割二值图'); figure,imshow(result2),title('切割原图');
完整代码
clear,clc,close all; f=imread('eye.jpg'); g=rgb2gray(f); bw=imbinarize(g,0.5); bw=~bw; imshow(bw),title('二值图'); rs=sum((bw')); figure; plot(rs); [h,w]=size(bw);%利用size函数可以返回图像的高和宽的值,方便下面控制循环结束 s=[];%新建空数组,以存储找到的每部分的边界值 a=1;b=1; while(a<h) %控制扫描所有的点 while(rs(a)==0&&a<h) %找到非0点的开始 a=a+1; end b=a; while(rs(b)>0) %找到非0点的结束 b=b+1; end s=[s;a b]; %将开始和结束点加入空数组 a=b; end slength=s(:,2)-s(:,1); [length,num]=max(slength); hresult=bw(s(num,1):s(num,2),:); [~,c]=find(hresult); result1=bw(s(num,1):s(num,2),min(c):max(c)); result2=f(s(num,1):s(num,2),min(c):max(c),:); figure,imshow(result1),title('切割二值图'); figure,imshow(result2),title('切割原图');
3. 切割眼睛简便方法
result = bwareaopen(bw, 500);
去除小对象函数,利用该函数去除所有连通区域中区域大小小于 500 的对象,第二个值的大小可以根据需要设定
clear,clc,close all;
f=imread('eye.jpg');
g=rgb2gray(f);bw=imbinarize(g,0.5);
bw=~bw;
imshow(bw),title('二值图');
result=bwareaopen(bw,500);
figure,imshow(result);