二值图边缘追踪
二值图边缘追踪,就是用该函数找出二值图中连通区域的边界。
borders = bwboundaries( bw );
该函数返回一个cell型数据,该类型的数据包括若干个矩阵,每个矩阵保存一个连通区域的边界点的坐标对。对于有坑洞的(如,下图齿轮中的圆形区域),将会分为两个矩阵来存储两个边界。注意,这个函数必须作用于二值图。对于下图使用该函数,得到的返回数据如下:
应用
1、用红线将下图齿轮的边框标记出来
思路:将图片转换为二值图,齿轮部分的值置为1,去除其他对象之后用 bwboundaries 找到齿轮的边界,用 plot 函数做标记。
步骤:
-
先将图片转换为二值图
从图中可以看到,齿轮的部分为黑色,我们需要先对图片取反,将齿轮部分转换为白色(1)。
-
对图片取反
matlab中的符号 ~ 是取反号,会把 1 取反为 0,0 取反为 1 。
代码:clc,clear,close all; f=imread('c.jpg'); imshow(f); g=rgb2gray(f); bw=~imbinarize(g); figure,imshow(bw);
-
去除小对象,去除齿轮旁边的东西
新增代码:bwao=bwareaopen(bw,300); figure,imshow(bwao);
-
用函数 bwboundaries 找到以上二值图中的齿轮边界信息。
新增代码:bs=bwboundaries(bwao);
从右侧工作区可以看到该返回值为12×1的cell型数据,下面我们打开 bs 变量查看数据,并打开其中的第一个矩阵查看:
可以看到cell型数据里有若干矩阵,每个矩阵保存的是边界的坐标对,我们可以借助于这些信息,在原图中表明这些坐标。
-
首先用 imshow 显示原图是为了用 hold on 和 plot 函数在图中做标记。下面用 for 循环遍历每一个边缘,然后用 plot 函数在每个边缘做标记。
size(bs,1);%计算 bs 第一列的长度
borders=bs{i};%将 bs 的第一个矩阵赋值给 borders
x=borders(:,2);%将borders的第二列的值赋值给 x,x 中保存的是该边界的列信息
y=borders(:,1);%将borders的第一列的值赋值给 y,y 中保存的是该边界的行信息,x和y中的每个同位置坐标组成一个坐标点
plot(x,y,‘r’,‘lineWidth’,2);%在第x列第y行的地方画红线 (r),线宽(lineWidth)为 2。(plot函数的用法详情请查看matlab帮助文件)
本例代码:clc,clear,close all; f=imread('c.jpg'); imshow(f); g=rgb2gray(f); bw=~imbinarize(g); figure,imshow(bw); bwao=bwareaopen(bw,300); figure,imshow(bwao); bs=bwboundaries(bwao); figure,imshow(f); for i=1:size(bs,1) borders=bs{i}; x=borders(:,2); y=borders(:,1); hold on; plot(x,y,'r','lineWidth',2); end
-
对于齿轮的边缘追踪函数,我们可以添加参数,使其只提取外部边界,对于上图来说,将齿轮内部的边界可以忽略。用这样的参数实现:
bs=bwboundaries(bwao,‘noholes’);
下图和上面的例子只改变了这个函数的参数。
2、用方框标记下方人脸
思路:先将图片转换为YCbCr图,找到人脸区域,然后本例介绍了一个函数可以在指定区域画方框,进行标记。
步骤:
- 先将图片转为YCbCr图,提取Cr分量图,将图片转换为二值图
代码:clc,clear,close all; f=imread('xu.jpg'); imshow(f); y=rgb2ycbcr(f); cr=y(:,:,3); cr(cr<140|cr>160)=0; cr(cr~=0)=255; bw=imbinarize(cr); figure,imshow(bw);
- 先用腐蚀将耳朵和脸部分开,填补脸上的小坑洞,再去除旁边的小对象。
新增代码:bwf=imfill(bw,'holes'); figure,imshow(bwf); bwe=imerode(bwf,strel('disk',5)); figure,imshow(bwf); bwao=bwareaopen(bwe,1100); figure,imshow(bwao);
- 先找到二值图中的非 0 元素,赋值给 r 和 c ,对于函数:
rectangle(‘position’,[min©,min®,w,h],‘EdgeColor’,‘r’);
表示在位置(position) 为 [最左侧位置坐标 , 最上方位置坐标 , 宽度 , 长度] 的地方,画颜色(EdgeColor)为红色(r)的方形。
因此我们在上面先计算了脸部的最宽的宽度和最高的高度,方便下面画方形。
本例代码:
clc,clear,close all; f=imread('xu.jpg'); imshow(f); y=rgb2ycbcr(f); cr=y(:,:,3); cr(cr<140|cr>160)=0; cr(cr~=0)=255; bw=imbinarize(cr); figure,imshow(bw); bwf=imfill(bw,'holes'); figure,imshow(bwf); bwe=imerode(bwf,strel('disk',5)); figure,imshow(bwf); bwao=bwareaopen(bwe,1100); figure,imshow(bwao); [r,c]=find(bwao); w=max(c)-min(c); h=max(r)-min(r); figure,imshow(f); hold on; rectangle('position',[min(c),min(r),w,h],'EdgeColor','r');
3、将下方所有人脸区域用方框标记
思路:先转换为YCbCr图,找到所有的人脸连通区域然后分别做标记。
步骤:
-
先转换为YCbCr图,分割出来Cr分量图,将肤色部分置为 1 ,其余置为 0 。再转换为二值图。
观察下图,可以看到有些衣服和肤色值范围较相近,因此被选择,我们可以调整肤色分布的范围,将肤色的检测范围缩小到 146-160。
-
调整肤色的检测范围,可以得到下图,可以发现衣服部分被去除了。
代码:clc,clear,close all; f=imread('girls.jpg'); imshow(f); y=rgb2ycbcr(f); cr=y(:,:,3); cr(cr<146|cr>160)=0; cr(cr~=0)=255; bw=imbinarize(cr); figure,imshow(bw);
-
然后先填充图中连通区域中的小坑洞。
新增代码:bwf=imfill(bw,'holes'); figure,imshow(bwf);
-
去除小对象。
新增代码:bwao=bwareaopen(bwf,1900); figure,imshow(bwao);
-
用 imboundaries 找到每个边界的坐标点,然后根据这些坐标点画方框。但是我们怎么根据上图找到人脸区域呢?这里给出一种方法,通过观察可以看到,人的头部的高和宽的比例都在1-2之间。我们便可以利用该信息在这些区域画框。
首先还是展示原图,然后 hold on 等待标记。根据每个连通区域坐标点的数值,计算出来长和宽,这个长和宽既可以用作画方框时候的参数,也可以用来判断这部分的高和宽的比。
本例代码:clc,clear,close all; f=imread('girls.jpg'); imshow(f); y=rgb2ycbcr(f); cr=y(:,:,3); cr(cr<146|cr>160)=0; cr(cr~=0)=255; bw=imbinarize(cr); figure,imshow(bw); bwf=imfill(bw,'holes'); figure,imshow(bwf); bwao=bwareaopen(bwf,1900); figure,imshow(bwao); bs=bwboundaries(bwao); imshow(f); hold on; for i=1:size(bs,1) borders=bs{i}; c=borders(:,2); r=borders(:,1); w=max(c)-min(c); h=max(r)-min(r); if(h/w>1&&h/w<2) rectangle('position',[min(c),min(r),w,h],'EdgeColor','r'); end end