Matlab不良图片检测系统
slandarer 人气:0不良图片检测部分
看到博主码猴小明用python PIL
库制作了一个不良图片识别系统,手痒,想用MATLAB也试试,毕竟矩阵运算也算是MATLAB的强项了,使用MATLAB写可比用python写简洁太多了,总体流程如下:
- 检查各个像素是否为肤色
- 将相邻的肤色像素归为一个皮肤区域,得到若干个皮肤区域,并剔除像素数量极少的皮肤区域
- 通过皮肤区域特点判定是否为不良图片
part.0 图片导入
imread
读取图片后需要double
一下,因为uint8
格式的图片数据会自动取整,没法保留小数。
% 读取图片 if nargin<1 path='test.jpg'; end picMat=imread(path); picMat=double(picMat);
本文使用的图片如下:
part.1 检查是否为肤色
这里懒得再去找了,直接使用大佬文中的判定条件,满足以下条件的将其判定为肤色:
- RGB颜色模式第一种:r > 95 and g > 40 and g < 100 and b > 20 and max([r, g, b]) - min([r, g, b]) > 15 and abs(r - g) > 15 and r > g and r > b
- RGB颜色模式第二种:nr = r / (r + g + b), ng = g / (r + g + b), nb = b / (r +g + b) ,nr / ng > 1.185 and r * b / (r + g + b) ** 2 > 0.107 and r * g / (r + g + b) ** 2 > 0.112
- HSV颜色模式:h > 0 and h < 35 and s > 0.23 and s < 0.68
- YCbCr颜色模式:97.5 <= cb <= 142.5 and 134 <= cr <= 176
网上各种肤色相关公式能找到很多,但是光照亮度不同,灯光颜色不同,肤色不同,各种影响因素太多,不可能100%准确:
% 可调节skinMethod为1/2/3/4 if nargin<2 skinMethod=3; end skinBool=[]; switch skinMethod case 1 % RGB颜色空间检测方法1 r=picMat(:,:,1); g=picMat(:,:,2); b=picMat(:,:,3); skinBool=(r>95)&(g>40)&(g<100)&(b>20)&(max(picMat,[],3)-min(picMat,[],3)>15)&(abs(r-g)>15)&(r>g)&(r>b); case 2 % RGB颜色空间检测方法2 nrgb=sum(picMat,3); r=picMat(:,:,1);nr=r./nrgb; g=picMat(:,:,2);ng=g./nrgb; b=picMat(:,:,3); skinBool=(nr./ng>1.185)&(r.*b./(nrgb.^2)>0.107)&(r.*g./(nrgb.^2)>0.112); case 3 % HSV颜色空间检测方法 hsvMat=rgb2hsv(double(picMat)./255); h=hsvMat(:,:,1); s=hsvMat(:,:,2); skinBool=(h>0)&(h<35/180)&(s>0.23)&(s<0.68); case 4 % YCbCr颜色空间检测方法 ycbcrMat=rgb2ycbcr(uint8(picMat)); cb=ycbcrMat(:,:,2); cr=ycbcrMat(:,:,3); skinBool=(cb>=97.5)&(cb<=142.5)&(cr>=134)&(cr<=176); end figure(); imshow(uint8(skinBool.*255));
可以看出仅对于本图片,HSV及YCbCr颜色空间检测方法相对较好:
part.2 皮肤区域标记
% 删除面积过小的区域 skinBool=bwareaopen(skinBool,50); % 获取每一个连通区域(皮肤区域) skinLabel=bwlabel(skinBool); skinLabel=sort(skinLabel(:))'; skinLabel(skinLabel==0)=[];
需要注意的是,此时skinLabel
标签形式为:
1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 4... ...
假如,1有4个说明被标记为1的区域面积为4,对该序列进行逐项做差能找出每个数值标签第一次出现的位置:
0 0 0 1 0 0 1 0 0 0 0 0 1 0 0... ...
例如2第一次出现在4+1=5的位置,3第一次出现在7+1=8的位置,那么2便一共出现过8-5=3次,那么找到每个数字首次出现位置并再次做差即可获取每种标签出现次数(面积),此方法舍去了for循环,使用向量化编程有了更快的速度:
Lpos=find([diff(skinLabel),1]); Larea=diff([0,Lpos]);
Larea
即为每个区域面积大小。
part.3 通过皮肤区域特点判定是否为不良图片
我们定义非色情图片的判定规则如下(满足任意一个判断为真):
- 皮肤区域的个数小于3个
- 皮肤区域的像素与图像所有像素的比值小于15%
- 最大皮肤区域小于总皮肤面积的45%
- 皮肤区域数量超过60个
% 皮肤区域的个数小于3个 flag1=length(Larea)<3; % 皮肤区域的像素与图像所有像素的比值小于15% flag2=sum(Larea)/numel(picMat(:,:,1))<0.15; % 最大皮肤区域小于总皮肤面积的45% flag3=max(Larea)/sum(Larea)<0.45; % 皮肤区域数量超过60个 flag4=length(Larea)>60; % 满足任意一项输出否 isBlue=~any([flag1,flag2,flag3,flag4]);
本文实例图片计算结果为否,即非不良图片!
完整代码
总共只有短短七十行!:
function isBlue=blueDetect(path,skinMethod) % 调用方式: % blueDetect(test.jpg) % 返回值isBlue为逻辑值true/false % 读取图片 if nargin<1 path='test.jpg'; end picMat=imread(path); picMat=double(picMat); %% ======================================================================== % 使用其他皮肤检测方法 % 可调节skinMethod为1/2/3/4 if nargin<2 skinMethod=3; end skinBool=[]; switch skinMethod case 1 % RGB颜色空间检测方法1 r=picMat(:,:,1); g=picMat(:,:,2); b=picMat(:,:,3); skinBool=(r>95)&(g>40)&(g<100)&(b>20)&(max(picMat,[],3)-min(picMat,[],3)>15)&(abs(r-g)>15)&(r>g)&(r>b); case 2 % RGB颜色空间检测方法2 nrgb=sum(picMat,3); r=picMat(:,:,1);nr=r./nrgb; g=picMat(:,:,2);ng=g./nrgb; b=picMat(:,:,3); skinBool=(nr./ng>1.185)&(r.*b./(nrgb.^2)>0.107)&(r.*g./(nrgb.^2)>0.112); case 3 % HSV颜色空间检测方法 hsvMat=rgb2hsv(double(picMat)./255); h=hsvMat(:,:,1); s=hsvMat(:,:,2); skinBool=(h>0)&(h<35/180)&(s>0.23)&(s<0.68); case 4 % YCbCr颜色空间检测方法 ycbcrMat=rgb2ycbcr(uint8(picMat)); cb=ycbcrMat(:,:,2); cr=ycbcrMat(:,:,3); skinBool=(cb>=97.5)&(cb<=142.5)&(cr>=134)&(cr<=176); end figure(); imshow(uint8(skinBool.*255)); %% ======================================================================== % 删除面积过小的区域 skinBool=bwareaopen(skinBool,50); % 获取每一个连通区域(皮肤区域) skinLabel=bwlabel(skinBool); skinLabel=sort(skinLabel(:))'; skinLabel(skinLabel==0)=[]; % 此时skinLabel标签形式为: % 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 4... ... % 假如,1有4个说明被标记为1的区域面积为4 % 对该序列进行逐项做差能找出每个数值标签第一次出现的位置: % 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0... ... % 进而可以获取每种标签所含点数(面积) % 此方法舍去了for循环使用向量化编程有了更快的速度: Lpos=find([diff(skinLabel),1]); Larea=diff([0,Lpos]); % 皮肤区域的个数小于3个 flag1=length(Larea)<3; % 皮肤区域的像素与图像所有像素的比值小于15% flag2=sum(Larea)/numel(picMat(:,:,1))<0.15; % 最大皮肤区域小于总皮肤面积的45% flag3=max(Larea)/sum(Larea)<0.45; % 皮肤区域数量超过60个 flag4=length(Larea)>60; % 满足任意一项输出否 isBlue=~any([flag1,flag2,flag3,flag4]); end
批量处理部分
那么我们能不能写一段代码,调用咱前面写的函数,检测文件夹A
内的全部图片,并将不良图片保存到B
文件夹方便我们扔进回收箱,将正常图片保存在C
文件夹方便观看呢?非常简单:
oriPath='.\A\';% 文件夹名称 BPath='.\B\'; % 放置不良图片的路径 nBPath='.\C\';% 放置正常图片的路径 % 若路径下不存在文件夹则创建文件夹 if ~exist(BPath,'dir') mkdir(BPath) end if ~exist(nBPath,'dir') mkdir(nBPath) end files=dir(fullfile(oriPath,'*.jpg')); picNum=size(files,1); %遍历路径下每一幅图像 for i=1:picNum fileName=strcat(oriPath,files(i).name); isBlue=blueDetect(fileName,3); if isBlue copyfile(fileName,strcat(BPath,files(i).name)); else copyfile(fileName,strcat(nBPath,files(i).name)); end end
那么我们来看看不良图片文件夹的分类效果?:
哈哈哈哈事实证明了解检测原理后构造一张符合条件的图实在是太容易,这种干扰情况过多的判断环境还是得整机器学习。
加载全部内容