本文主要使用 YCbCr 颜色模型根据肤色提取相关内容。
在前面讲到 YCbCr 颜色模型时说过,Cr 分量图中像素点的值在 140-160 范围内的像素点和人的肤色非常接近,因此可以使用该分量图进行和肤色相关内容的提取。下面介绍了几个关于该用法的例子。
1. 根据肤色提取图中的手
思路:将图片先转换为 YCbCr 图片,然后提取出来 Cr 分量图,筛选出来其中值在140-160 的像素点。将这些像素点置为 255,其他像素点置为 0。再转换为二值图,用二值图点乘原图,提取出该部分的彩色图。
步骤:
-
先将图片转换为YCbCr图片,然后提取出来 Cr 分量图
代码:clc,clear,close all; f=imread('hand.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr);
-
将分量图中值在140-160 部分的像素点的值置为 255,其他部分的值置为 0
新增代码:cr(cr<140|cr>160)=0; %将分量图中值在140-160范围之外的像素点置为0 cr(cr~=0)=255; %将剩余的值在140-160的点置为255 figure,imshow(cr);
-
将图片转换为二值图
新增代码:bw=imbinarize(cr); figure,imshow(bw);
-
观察上图可以发现在手的范围内还有很多黑色部分,因此要先去除小坑洞
新增代码:
bw=imfill(bw,'holes'); figure,imshow(bw);
-
下面的问题便是将上面白色区域在原图中对应的区域提取出来。这里应用一种点乘的方法。
首先对于上一步得到的 bw 图片来说,手所在区域的值都是 1 ,而其他部分的值都是 0 。我们可以用这张图的每个像素点和原图对应位置的像素点的值相乘,这样对于手的区域的像素点,留下的是原有的值,对于其他部分得到的是 0 。
matlab中有运算符
.*
,可以实现对于相同维度相同大小的图片,将对应位置的每个点的值相乘。由于上面得到的 bw 图是二维的,因此我们再次用cat
函数将上图重叠三层,得到三维的新图,将新图和原图相点乘。但是在matlab中的运算都会转换为 double 类型的数据,因此在做点乘时候还需要将原图转换为double类型的。对于得到的结果还需要将其转换为8位图(用函数
uint8
)
本例代码:clc,clear,close all; f=imread('hand.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr); cr(cr<140|cr>160)=0; cr(cr~=0)=255; figure,imshow(cr); bw=imbinarize(cr); figure,imshow(bw); bw=imfill(bw,'holes'); figure,imshow(bw); bws=cat(3,bw,bw,bw); result=double(f).*bws; figure,imshow(uint8(result));
2. 截取图片中的人脸头像
思路:通过观察可以发现,该图的操作方式和上一个例子差不多,只需要将后几步改为切割即可。
步骤:
-
转换,分离,填补小坑洞。这几部分代码和上面的一样。
代码:clc,clear,close all; f=imread('qqm.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr); cr(cr<140|cr>160)=0; cr(cr~=0)=255; figure,imshow(cr); bw=imbinarize(cr); figure,imshow(bw); bw=imfill(bw,'holes'); figure,imshow(bw);
-
观察上图可以发现有其它的连通区域干扰,我们用函数
bwareaopen
去除这些连通区域。
新增代码:bwao=bwareaopen(bw,500); figure,imshow(bwao);
-
用
find
函数找到图中非0点的坐标点,然后再进行切割。
本例代码:clc,clear,close all; f=imread('qqm.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr); cr(cr<140|cr>160)=0; cr(cr~=0)=255; figure,imshow(cr); bw=imbinarize(cr); figure,imshow(bw); bw=imfill(bw,'holes'); figure,imshow(bw); bwao=bwareaopen(bw,500); figure,imshow(bwao); [r,c]=find(bwao); resual=f(min(r):max(r),min(c):max(c),:); figure,imshow(resual);
3. 将图片中的头和两只手截取出来
思路:本例对肤色的提取和上面类似,改变点在于如何将图中的三部分都截取出来,本例用了一种方法见下所示,大家可以先尝试自己动手,用自己的方法来解决。
步骤:
-
首先将图片的cr分量图分离,然后将肤色部分找出来,转换为二值图。
代码:clc,clear,close all; f=imread('man.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr); cr(cr<140|cr>160)=0; cr(cr~=0)=255; figure,imshow(cr); bw=imbinarize(cr); figure,imshow(bw);
-
观察二值图,我们发现在脸的下方有多余的部分和脸直接相连,我们需要去除,以防止对后续的操作造成影响。
我是直接用腐蚀,便将脸部下面相连的圆圈直接去除了(如果和脸部下方相连的是一大片区域,该怎么去除呢?大家可以思考一下,在本例末尾我会说一下我的思路。)。
新增代码:bw=imerode(bw,strel('disk',1)); figure,imshow(bw);
-
我们可以发现在中间手指头的食指上有黑色区域,我们可以用膨胀去除,同时可以恢复上一步腐蚀掉的一点区域。
新增代码:bw=imdilate(bw,strel('disk',1)); figure,imshow(bw);
-
然后我们去除连通区域中的小坑洞。
新增代码:bw=imfill(bw,'holes'); figure,imshow(bw);
-
再去除图中的小对象。
新增代码:bwao=bwareaopen(bw,500); figure,imshow(bwao);
-
可以看到图中就只剩下手和头的区域了,之后我选择了标记连通分量的方法来标记每个连通区域,再用
for
循环来切割每个区域。关于标记连通分量的函数label
的详细讲解,请看这里。
本例代码:clc,clear,close all; f=imread('man.jpg'); imshow(f); fy=rgb2ycbcr(f); cr=fy(:,:,3); figure,imshow(cr); cr(cr<140|cr>160)=0; cr(cr~=0)=255; figure,imshow(cr); bw=imbinarize(cr); figure,imshow(bw); bw=imerode(bw,strel('disk',1)); figure,imshow(bw); bw=imdilate(bw,strel('disk',1)); figure,imshow(bw); bw=imfill(bw,'holes'); figure,imshow(bw); bwao=bwareaopen(bw,500); figure,imshow(bwao); [L,n]=bwlabel(bwao); for i=1:n [r,c]=find(L==i); resual=f(min(r):max(r),min(c):max(c),:); figure,imshow(resual); end
对于第二步的问题,我的思路为:先用腐蚀将两个部分断开,然后用去除小对象将下方区域去除,再用膨胀将脸部恢复原大小。