赞
踩
《数字图像处理》
班 级:数字媒体技术2020级1班
姓 名:快乐的小蓝
学 号:XXXXXXXXX
XXXX大学信息学院
目录
选择了简易美图秀秀,使用matlab的appdesigner实现了功能交互界面,功能模块具体有定位人脸、磨皮、祛痘、大眼,缩小鼻翼等人脸处理功能。
主要是参照美图秀秀和平时学习相关的算法,实现了一个基础版的美图秀秀,但是平时所学还不足以支撑起这样一个项目,所以我就根据自己要实现的功能,查阅了大量的资料和相关代码实现,进行了效果测试,以及代码的修改。
我在网上查了很多帖子,不涉及深度学习的算法主要有3种
1.检测最大连通域
原理:通过对图像进行形态学的处理,形成多个连通区域,默认最大的连通区域为人脸所在的区域。
缺陷:
1)有时候静态的图片,经过形态学处理之后,最大的连通域并不是人脸的部分,所以这个算法局限性比较大。
2)只能对图像中的一个人脸进行检测
原理:肤色检测的方法有很多,但是无论是基于不同的色彩空间还是不同的肤色模型,其根本出发点在于肤色分布的聚集性,即肤色的分量一般聚集在某个范围内。不同的色彩空间肤色的分布范围不同。
1)RGB彩色空间
据统计资料,再均匀光照下,肤色分布范围在:R>95且G>40且B>20且max{R,G,B}-min{R,G,B}>15,且|R-G|>15且R>G且R>B
2)YCbCr彩色空间
据资料统计,肤色在YCbCr彩色空间的分布范围为77<=Cb<=127,133<=Cr<=173,下面就是使用的这个彩色空间进行基于肤色的人脸检测,并且肤色模型用的是阈值模型,而没有选择高斯模型
3)HSV彩色空间
HSV空间建立的肤色检测模型要求满足0度<H<=25度或335度<=H<=360度且0.2<=S=0.6且0.4<=V
缺陷:
级联对象检测器使用Viola-Jones算法来检测人的面部,鼻子,眼睛,嘴巴或上半身。还可以使用Image Labeler训练自定义分类器以与此System对象一起使用。这个工具进行人脸定位比较准确,而且能实现嘴巴、鼻子、眼睛的轻松定位,在一张图片中可以定位出多个人脸。
这个工具用算法都比较复杂,根据在网上查阅的博文,我有了大致的认识,总结如下:
这个检测器用的算法是V-J算法,这个算法是以提出这个算法的两位大佬命名的,Viola和Jones。实质是,在AdaBoost算法的基础上,使用Haar-like小波特征(简称haar特征)和积分图的方法进行的人脸检测。首先来看AdaBoost算法,这个算法本质上是将多个弱分类器,组合成强的分类器。
那么AdaBoost的训练样本从何而来呢,那么就要说到Haar-like小波特征,虽然每个人都长的不一样,但是人脸都有一些固定的特征,比如眼睛部分比较暗,鼻子两侧的比较暗,但中间部分比脸颊亮,嘴巴部分也比较暗,根据这些特征,V-J用了四种矩形特征,用黑色部分的灰度值减去白色部分的灰度值,就产生了人脸的矩形特征。一幅24*24的图像就有160000个特征矩形,如果对这些特征矩形中所有像素点一次遍历求和,计算量非常大,这时候积分图就出现了。
积分图,表征为位于(i,j)位置的像素点的积分是左上方所有像素的和,如果知道某些像素点的积分,求某个区域的像素和,只需要进行简单的几次加减,不需要每次遍历。所以一张图,将每个像素点都表征为积分,将积分值存储在一个和原图像大小相同的矩阵中,求指定区域的像素和的时候,只需要找出对应位置的积分进行加减,就能得到这个区域的像素和,计算量大大减少了。
缺陷:有时候也会定位出不是嘴巴的部分,或者会将眼睛定位成嘴巴。
总的来说,按照效果排列:V-J算法>最大连通域>肤色检测。
第一个是检测最大连通域的人脸检测代码:
检测最大连通区域
- rgb = Img;
-
- I = rgb2gray(rgb);%灰度化
-
- [n1,n2] = size(I);%获取图像矩阵大小
-
- %灰度图
-
- % figure,imshow(I),title('灰度图')
-
- tic
-
- avel=fspecial('average',3);
-
- I=filter2(avel,I)/255;
-
- % figure,imshow(I),title('均值滤波')
-
- BW = imbinarize(I);
-
- % figure,imshow(BW),title('二值化')
-
- B = ones(21);%结构元素
-
- BW = -imerode(BW,B) + BW;
-
- % BW=imdilate(BW,B)-imerode(BW,B);
-
- % figure,imshow(BW),title('形态学边界提取')
-
- BW = bwmorph(BW,'thicken');
-
- % figure,imshow(BW),title('加粗边界')
-
- BW = ~(bwareaopen(~(BW), 50));%使用bwareaopen 函数删除包含的像素数少于50的对象
-
- % figure,imshow(BW),title('把空洞填了')
-
-
-
- %进行形态学运算
-
- B = strel('line',50,90);
-
- BW = imdilate(BW,B);
-
- BW = imerode(BW,B);
-
- %figure,imshow(BW),title('闭运算操作之后')
-
- B = strel('line',10,0);
-
- BW = imerode(BW,B);
-
- figure,imshow(BW),title('闭操作之后再腐蚀')
-
- BW = gpuArray(BW);%%将数据载入gpu可以加速,电脑不一定支持
-
-
-
- %最小化背景
-
- %细分
-
- div = 10;
-
- r = floor(n1/div);%分成10块 行
-
- c = floor(n2/div);%分成10块 列
-
- x1 = 1;x2 = r;%对应行初始化
-
- s = r*c;%块面积
-
- %判断人脸是否处于图片四周,如果不是就全部弄黑
-
- for i=1:div
-
- y1 = 1;y2 = c;%对应列初始化
-
- for j=1:div
-
- loc = find(BW(x1:x2,y1:y2)==0);%统计这一块黑色像素的位置
-
- num = length(loc);
-
- rate = num*100/s;%统计黑色像素占比
-
- if (y2<=0.2*div*c||y2>=0.8*div*c)||(x1<=r||x2>=r*div)
-
- if rate <=100
-
- BW(x1:x2,y1:y2) = 0;
-
- end
-
- imshow(BW)
-
- else
-
- if rate <=25
-
- BW(x1:x2,y1:y2) = 1;
-
- end
-
- %imshow(BW)
-
- end%下一列
-
- y1 = y1 + c;
-
- y2 = y2 + c;
-
- end%下一行
-
- x1 = x1 + r;
-
- x2 = x2 + r;
-
- end
-
-
-
- figure
-
- subplot(1,2,1)
-
- imshow(BW)
-
- title('最终处理')
-
- L = bwlabel(BW,8);%利用belabel函数对8连通域区间进行标号
-
- BB = regionprops(L,'BoundingBox');%得到矩形框,框出每一个连通域
-
- BB = cell2mat(struct2cell(BB));
-
- [s1,s2] = size(BB);
-
- BB = reshape(BB,4,s1*s2/4)';
- pickshape = BB(:,3)./BB(:,4);%
- shapeind = BB(0.3<pickshape&pickshape<3,:);%筛选掉尺寸比例不合格
- [~,arealind] = max(shapeind(:,3).*shapeind(:,4));
- subplot(1,2,2)
- imshow(rgb)
- hold on
- rectangle('Position',[shapeind(arealind,1),shapeind(arealind,2),shapeind(arealind,3),shapeind(arealind,3)],...
- 'EdgeColor','g','Linewidth',2)
- title('人脸检测')
第二个是基于YCbCr颜色空间的人脸检测
代码块:
- %肤色检测模型
-
- image = imread('person10.jpg');
-
- %三通道分离
-
- image_red = image(:,:,1);
-
- image_green = image(:,:,2);
-
- image_blue = image(:,:,3);
-
- [ROW,COL] = size(image_red);
-
- figure
-
- imshow(image);
-
- title('原图');
-
-
-
- YCbCr = rgb2ycbcr(image);%将 RGB 图像的红色、绿色和蓝色值转换为 YCbCr 图像的亮度 (Y) 和色度(Cb 和 Cr)值。
-
-
-
- Y = YCbCr(:,:,1);
-
- Cb = YCbCr(:,:,2);
-
- Cr = YCbCr(:,:,3);
-
- pic_gray = zeros(ROW,COL);%创建和原图大小相同的全零矩阵
-
-
-
- for i = 1:ROW
-
- for j = 1:COL
-
- if(Cb(i,j) > 77 && Cb(i,j) < 127 && Cr(i,j) > 133 && Cr(i,j) < 173)
-
- pic_gray(i,j) = 255;
-
- else
-
- pic_gray(i,j) = 0;
-
- end
-
- end
-
- end
-
-
-
- figure
-
- imshow(pic_gray);
-
- title('肤色检测图');
-
-
-
- se = strel('square',20);
-
- erode = imerode(pic_gray,se);
-
- figure
-
- imshow(erode);
-
- title('形态学滤波');
-
-
-
- x_min = 1024;
-
- x_max = 0;
-
- y_min = 768;
-
- y_max = 0;
-
-
-
-
-
- for i = 1:ROW
-
- for j = 1:COL
-
- if(erode(i,j) > 0 && x_min > j)
-
- x_min = j;
-
- end
-
- if(erode(i,j) > 0 && x_max < j)
-
- x_max = j;
-
- end
-
- end
-
- end
-
-
-
- for i = 1:ROW
-
- for j = 1:COL
-
- if(erode(i,j) > 0 && y_min > i)
-
- y_min = i;
-
- end
-
- if(erode(i,j) > 0 && y_max < i)
-
- y_max = i;
-
- end
-
- end
-
- end
-
-
-
- for i = 1:ROW
-
- for j = 1:COL
-
- if(j == x_min && i >= y_min && i <= y_max)
-
- image_test(i,j,1) = uint8(255);
-
- image_test(i,j,2) = uint8(255);
-
- image_test(i,j,3) = uint8(255);
-
- elseif(j == x_max && i >= y_min && i <= y_max)
-
- image_test(i,j,1) = uint8(255);
-
- image_test(i,j,2) = uint8(255);
-
- image_test(i,j,3) = uint8(255);
-
- elseif(i == y_max && j >= x_min && j <= x_max)
-
- image_test(i,j,1) = uint8(255);
-
- image_test(i,j,2) = uint8(255);
-
- image_test(i,j,3) = uint8(255);
-
- elseif(i == y_min && j >= x_min && j <= x_max)
-
- image_test(i,j,1) = uint8(255);
-
- image_test(i,j,2) = uint8(255);
-
- image_test(i,j,3) = uint8(255);
-
- else
-
- image_test(i,j,1) = image(i,j,1);
-
- image_test(i,j,2) = image(i,j,2);
-
- image_test(i,j,3) = image(i,j,3);
-
- end
-
- end
-
- end
-
- %subplot(2,2,4);
-
- figure
-
- imshow(image_test);
-
- title('头像检测');
第三个是利用matlab工具来进行人脸的定位
代码块:
- detector = vision.CascadeObjectDetector;%获取检测器
-
- %Read the input image
-
- I = imread('person.jpg');%读入图像
-
- face_dtect = step(detector,I);%人脸检测,返回人脸区域的位置向量
-
- figure
-
- imshow(I)%显示图像
-
- hold on
-
- for i = 1:size(face_dtect,1)%检测器可能检测到多个
-
- rectangle('Position',face_dtect(i,:),'LineWidth',1,'LineStyle','-','EdgeColor','m');%用矩形标注出人脸区域
-
- end
-
- title('人脸检测');
肤色检测:
结果分析:
对于背景和人物衣物颜色与肤色阈值不相近的图像来说,效果比较好,但是大部分测试图像定位都不准确,尤其是人脸区域很小,或者人物的衣物与背景和肤色阈值接近,效果就很差。
最大连通域:
结果分析:最大连通域默认整个图像的最大连通域就是人脸,所以对于人脸占图像比例大的图片,处理效果就很好(也不是越大越好,人脸尽量要完整),但是像第二张图片,整个小姑娘站的较远,且是侧身站的,人脸部分出现了较大的断裂,无法连通,所以检测出来的结果完全错误。但总体来说测试效果成功的概率比肤色检测好很多。
V-J算法:
结果分析:
这个算法人脸定位比较准确,甚至还可以定位五官和上半身。对正脸的图像准确率高一些,人脸部分可以比较小,但必须完整。上面的倒数两张图定位失败了,倒数第二张定位出了两张人脸,人物的衣服结构复杂,检测器把它也作为人脸了,最后一张直接没有检测出人脸,误差比较大,而且,我在测试检测五官的时候,我发现这个检测器在检测嘴巴的时候,往往将眼睛也标记出来了,但是检测鼻子比较精准。但是整体效果比前面两种算法好很多。
磨皮:实质上就是图像的平滑,根据目前的学到的平滑图像的算法,空域有高斯滤波、中值滤波、均值滤波,频域就是低通滤波器。我在测试的时候,发现这个滤波器会模糊五官和边界,效果很不理想。翻看教材,发现教材上使用了双边滤波进行人脸的磨皮。看书上的公式脑袋晕乎乎的,但是听了专业同学的讲解,发现这个算法也不难。
这算法是在高斯滤波上的改进,高斯滤波只考虑了位置对像素值的影响,即位置越近,对像素值的影响越大,所以这样会模糊掉边界等重要信息。而双边滤波,将位置和像素值看得同等重要,在平坦区域,像素的位置主导双边滤波的效果。在边缘区域,由于边缘两侧像素值相差很大,所以边缘另一侧的像素权重降低,几乎不会影响到边缘这一侧的像素,这就实现了保边的效果。
- %用双边滤波实现人脸美化的效果
-
- I = imread('person2.jpg');%读取图像
-
- figure(1)
-
- imshow(I),title('原图');%显示原图
-
- bfilt_rgb(I,15,6,0.1);%调用双边滤波函数,进行滤波处理
-
- %函数实现
-
- function g = bfilt_rgb(f,r,a,b)
-
- % f彩色图;r滤波半径;a全局方差;b局部方差
-
- [x,y] = meshgrid(-r:r);%创建二维网格坐标
-
- w1 = exp(-(x.^2+y.^2)/(2*a^2));%计算滤波内的空间权值
-
- f = im2double(f);%将uint8转换成double,方便后续处理
-
- h = waitbar(0,'Applying bilateral filter...');%设置进度条
-
- set(h,'Name','Bilateral Filter Progress');
-
-
-
- detector = vision.CascadeObjectDetector;%目标探测器
-
- facePart = step(detector,f);
-
- %获取脸部坐标
-
- X_min = facePart(1,1);%人脸区域的左下x坐标
-
- Y_min = facePart(1,2);%y坐标
-
- Width = facePart(1,3);%区域宽度
-
- Height = facePart(1,4);%区域高度
-
- %三通道分离
-
- fr = f(:,:,1);
-
- fg = f(:,:,2);
-
- fb = f(:,:,3);
-
- [m,n] = size(fr);%图像矩阵大小,方便后面像素的遍历
-
- fr_temp = padarray(fr,[r r],'symmetric');%镜像填充矩阵,方便处理边界像素
-
- fg_temp = padarray(fg,[r r],'symmetric');%镜像填充矩阵,方便处理边界像素
-
- fb_temp = padarray(fb,[r r],'symmetric');%镜像填充矩阵,方便处理边界像素
-
- [gr,gg,gb] = deal(zeros(size(fr)));%创建三个和原图像大小相同的全零矩阵
-
-
-
- for i = r+1+Y_min:Height+Y_min+r%对人脸部分进行双边滤波
-
- for j = r+1+X_min:X_min+Width+r
-
- temp1 = fr_temp(i-r:i+r,j-r:j+r);%三通道的滤波区域31X31的正方形
-
- temp2 = fg_temp(i-r:i+r,j-r:j+r);
-
- temp3 = fb_temp(i-r:i+r,j-r:j+r);
-
- dr = temp1 - fr_temp(i,j);
-
- dg = temp2 - fg_temp(i,j);
-
- db = temp3 - fb_temp(i,j);
-
- w2 = exp(-(dr.^2+dg.^2+db.^2)/(2*b^2));%求滤波内每个像素对目标像素的影响权值(基于像素值)
-
- w = w1.*w2;%将两种权值相加,求得滤波范围内每个像素的最终权值
-
- gr(i-r,j-r) = sum(sum(temp1.*w))/sum(w(:));%将权值与对应像素值相乘相加,再除以权值和,即求得该像素点经双边滤波后的像素值
-
- gg(i-r,j-r) = sum(sum(temp2.*w))/sum(w(:));
-
- gb(i-r,j-r) = sum(sum(temp3.*w))/sum(w(:));
-
- end
-
- waitbar((i-r)/n);%进度条
-
- end
-
- %将人脸部分赋黑,方便后面图像的叠加
-
- for m=X_min+2:X_min+Width
-
- for n=Y_min+1:Height+Y_min
-
- f(n,m,1)=0;
-
- f(n,m,2)=0;
-
- f(n,m,3)=0;
-
- end
-
- end
-
- g = cat(3,gr,gg,gb);%串联数组,合并三通道
-
- result=imadd(g,f);
-
- figure(2)
-
- imshow(result),title('双边滤波');%显示处理图
-
- end
效果分析:双边滤波既平滑了皮肤,又保留了五官部分,效果真的很好,就是处理速度慢一些。
首先是利用matlab工具箱的人脸检测器,检测出两只眼睛的位置向量,然后分别对两只眼睛进行放大处理:先根据每只眼睛的位置向量确定放大区域的中心坐标,然后设置放大区域的边长,遍历需要放大的区域,使用最邻近插值法,得到放大后的像素值,遍历完成之后,放大效果就实现了。
- %放大眼睛
-
- I = imread('person13.jpg');%读入图像
-
- I = im2double(I);%数据类型转化,以便后续处理
-
- %V-J算法检测眼睛
-
- detector = vision.CascadeObjectDetector;%调用检测器
-
- detector.ClassificationModel='LeftEyeCART';%调用眼睛检测器
-
- bboxes = detector(I);%检测眼睛,返回眼睛区域的位置向量
-
- %放大眼睛
-
- strength=20;%放大强度
-
- pointx=floor(bboxes(1,2)+(bboxes(1,3)/2));%根据检测器大致确定眼睛中心点
-
- pointy=floor(bboxes(1,1)+(bboxes(1,4)/2))+10;
-
- r=50;%放大区域边长
-
- left=pointy-r;
-
- right=pointy+r;
-
- top=pointx-r;
-
- bottom=pointx+r;
-
- space=r*r;%放大的正方形区域
-
- I1=I;%将原图缓存,存储处理后的图像
-
- for x=top:bottom%遍历放大区域的像素
-
- offsetx=x-pointx;
-
- for y=left:right
-
- offsety=y-pointy;
-
- xy=offsetx*offsetx+offsety*offsety;
-
- if xy<=space
-
- scale=1-xy/space;
-
- scale=1-strength/100*scale;
-
- %减少计算量,采用最邻近插值,利用四舍五入函数实现
-
- posy=round(offsety*scale+pointy);%放大后的Y坐标值
-
- posx=round(offsetx*scale+pointx);%放大后的X坐标值
-
- %分别对三通道进行处理
-
- I1(x,y,1)=I(posx,posy,1);
-
- I1(x,y,2)=I(posx,posy,2);
-
- I1(x,y,3)=I(posx,posy,3);
-
- end
-
- end
-
- end
-
-
-
- pointx=floor(bboxes(2,2)+(bboxes(2,3)/2));%对另一只眼睛进行同样的处理
-
- pointy=floor(bboxes(2,1)+(bboxes(2,4)/2));
-
- r=50;
-
- left=pointy-r;
-
- right=pointy+r;
-
- top=pointx-r;
-
- bottom=pointx+r;
-
- space=r*r;
-
- I2=I1;
-
- for x=top:bottom
-
- offsetx=x-pointx;
-
- for y=left:right
-
- offsety=y-pointy;
-
- xy=offsetx*offsetx+offsety*offsety;
-
- if xy<=space
-
- scale=1-xy/space;
-
- scale=1-strength/100*scale;
-
- posy=round(offsety*scale+pointy);
-
- posx=round(offsetx*scale+pointx);
-
- I2(x,y,1)=I1(posx,posy,1);
-
- I2(x,y,2)=I1(posx,posy,2);
-
- I2(x,y,3)=I1(posx,posy,3);
-
- end
-
- end
-
- end
-
- figure
-
- subplot(121),imshow(I2),title('放大眼部');
-
- subplot(122),imshow(I),title('原图');
效果分析:因为这个功能主要是基于人眼的位置识别,所以人眼位置定位是否准确,直接影响了眼部增大功能的实现效果,对于有些人像图,甚至检测不到眼部,所以就根本不能使用该功能,还有些人像,会将嘴巴部分也识别成眼部,所以会导致嘴巴部分被放大,但是只要是能准确定位眼部的,处理效果就很不错。
这个算法依旧是基于人脸检测V-J和区域放缩算法,并且步骤与眼睛放大大同小异。首先使用matlab定位到鼻子的位置,然后根据工具返回的位置向量确定鼻子的中心坐标,最后使用区域放大算法,只不过将强度改为负数,就达到了区域缩小的效果,即鼻头缩小术。
- %瘦鼻
-
- I = imread('person25.jpg');
-
- detector = vision.CascadeObjectDetector;%检测器
-
- detector.ClassificationModel='Nose';
-
- detector.MergeThreshold=10;%增加合并阈值
-
- bboxes=detector(I);
-
- Nose=insertObjectAnnotation(I,'rectangle',bboxes,'Nose');%用矩形框标出鼻子定位的矩形区域
-
- figure(9);imshow(Nose);title('Nose'); %显示图像
-
- %区域图像缩小
-
- strength=-20;%缩小强度
-
- pointx=floor(bboxes(1,2)+(bboxes(1,3)/2));%根据检测器大致确定鼻子中心点
-
- pointy=floor(bboxes(1,1)+(bboxes(1,4)/2));
-
- r=50;%放大区域边长
-
- left=pointy-r;
-
- right=pointy+r;
-
- top=pointx-r;
-
- bottom=pointx+r;
-
- space=r*r;%缩小的正方形区域
-
- I1=I;%将原图缓存,存储处理后的图像
-
- for x=top:bottom%遍历缩小区域的像素
-
- offsetx=x-pointx;
-
- for y=left:right
-
- offsety=y-pointy;
-
- xy=offsetx*offsetx+offsety*offsety;
-
- if xy<=space
-
- scale=1-xy/space;
-
- scale=1-strength/100*scale;
-
- %减少计算量,采用最邻近插值,利用四舍五入函数实现
-
- posy=round(offsety*scale+pointy);%放大后的Y坐标值
-
- posx=round(offsetx*scale+pointx);%放大后的X坐标值
-
- %分别对三通道进行处理
-
- I1(x,y,1)=I(posx,posy,1);
-
- I1(x,y,2)=I(posx,posy,2);
-
- I1(x,y,3)=I(posx,posy,3);
-
- end
-
- end
-
- end
-
- figure
-
- imshow(I1);
效果分析:这个功能几乎跟放大眼睛的功能相同,所以只要鼻子定位准确,鼻翼缩小的效果是很好的,但是对于定位不到鼻子的人像,就无法进行缩小处理了,而且我发现了一个神奇的现象,原来鼻头变小,会有视觉上放大眼睛的效果。
这个算法主要是基于像素平移,我参考的是网上有一篇液化算法的帖子,里面给出了逆变换公式和算法实现的步骤,没有给出实现代码,所以,我一开始对这个算法理解不深的时候,根本无法写出实现的代码,但是后面我再次去看这个算法的时候,发现这个算法实际上就是像素平移,因为平移的像素到达目的位置的时候,由于根据变换公式算出来的坐标是双精度的,而图像的矩阵的坐标是正整数,所以平移的时候,无论用什么插值算法,都有可能出现像素的重叠或者缺失像素值的问题,所以采用逆变换,用目的位置来倒推原来的像素的位置,因为倒推出来也是双精度,所以对原来像素值邻近像素使用双线性插值算法,算出的像素值就是目的位置上的像素值,这样就实现了像素比较平滑的平移。算法实现之后,我用的是交互式选取圆形区域,然后再选取一个平移的圆心,目的是对脸部边缘的像素平滑平移,从而从视觉效果上实现瘦脸的效果,但实际上操作起来,一旦选取的区域与目的圆心的位置相距太远,就会产生畸变,导致脸部严重变形,我尝试了很多人脸图像,只有一张效果还不错,但是也是要经过很多次像素的不明显平移,才会看出比较明显的瘦脸效果。我一直在想怎么完善一下,但是时间好像不够了,就放弃了。
- for cou =1:300
-
- if cou==1
-
- I = imread('C:\Users\leishen\Desktop\数字图像处理\期末大作业\测试效果\person7.jpg');%读入图片
-
- else
-
- I = imread('C:\Users\leishen\Desktop\数字图像处理\期末大作业\测试效果\person25.jpg');%读入修改之后的图片,以便继续进行瘦脸处理
-
- end
-
-
-
- figure
-
- imshow(I);
-
- h = drawcircle('Color','k','FaceAlpha',0.4);%交互式选取圆形区域
-
- M = ginput(1); %交互式选取坐标,即变化后的圆心的位置
-
- [m,n,~] = size(I);%图像矩阵的大小
-
- center = [floor(h.Center(2)),floor(h.Center(1))];%变形前的圆心
-
- radius = h.Radius;%变形的圆半径
-
-
-
- RGB_buff = I;
-
- %遍历圆形选区的每一个像素
-
- for i=1:m
-
- for j=1:n
-
- distant = sqrt((i-center(1)).^2+(j-center(2)).^2);%判断该像素点是否在选取的圆形区域内
-
- if distant <= radius
-
- x = [i,j];%变形后的坐标
-
- U = x - ((radius^2 - (x - center).^2) / (radius^2 - (x - center).^2 + (M - center).^2))^2 * (M - center);%逆变换公式
-
- uu = floor(U);%舍弃小数部分,保留整数
-
- ab = U-uu;%这个数据用于后面双线性插值处理
-
- a = ab(1);
-
- b = ab(2);
-
- m1 = uu(1);
-
- n1 = uu(2);
-
- for k=1:3
-
- I(i,j,k)=(1-a)*(1-b)*RGB_buff(m1,n1,k)+a*(1-b)*RGB_buff(m1+1,n1,k)+(1-a)*b*RGB_buff(m1,n1,k)+a*b*RGB_buff(m1+1,n1+1,k);%双线性插值
-
- end
-
- end
-
- end
-
- end
-
- figure
-
- imshow(I);
-
-
-
- answer = questdlg('是否要继续调整', ...
-
- '选择', ...
-
- 'YES','NO','NO');
-
-
-
- switch answer
-
- case 'YES'
-
- imwrite(I,'C:\Users\leishen\Desktop\数字图像处理\期末大作业\测试效果\person25.jpg');
-
- case 'NO'
-
- msgbox("退出调整");
-
- break;
-
- end
-
-
-
- end
可以看到脸部的左边实现了较大的像素平移效果,所以从视觉上有了瘦脸的效果,但是仔细看,就会发现像素平移造成了图像的局部畸变和模糊。
这个实现原理比较简单,先交互式选取想要祛痘的区域,然后提取这个区域的左上角的第一个像素,用这个像素值-5到像素值+5范围的像素值,随机的填充选取的区域。然后为了减弱填充的边界,用了高斯滤波对脸部进行了高斯滤波的处理。
- g=imread('person1.jpg');
-
- % n 为操作次数,默认为1,g为原图
-
- n=3;
-
- g2 = g;
-
- %得到原图像的大小
-
- [ M,N,~ ] = size( g );
-
- while( n ~=0 )
-
- n = n - 1;
-
- %进行交互选择处理区域
-
- mask = roipoly( g2 );%roipoly函数--指定多边形感兴趣区域,将蒙版作为二进制图像返回,所以下面的乘法就可以得到原图像被提取的区域,因为0的部分乘以任何像素值都为0,1乘以原像
-
- %素还是等于原像素
-
- x1 = immultiply( mask,g2( :,:,1 ) );
-
- x2 = immultiply( mask,g2( :,:,2 ) );
-
- x3 = immultiply( mask,g2( :,:,3 ) );
-
- x = cat( 3,x1,x2,x3 );%将x1和x2和x3沿第三维度串联起来,这里相当于三通道合并
-
- % figure
-
- % imshow(x);%原来x是交互时提取的区域
-
- %预分配内存,f1,f2,f3存储三个通道的运算结果
-
- f1 = zeros( M,N );
-
- f2 = zeros( M,N );
-
- f3 = zeros( M,N );
-
-
-
- %跳出双重循环设置flag
-
- flag = 0;
-
- %找到第一个像素值不为0的点,得到该点像素值,作为采样后填充的像素
-
- for i = 1:M
-
- for j = 1:N
-
- if( x1( i,j ) ~= 0 )
-
- r = x( i,j,: );
-
- flag = 1;%找到之后,跳出循环
-
- break
-
- end
-
- if( flag == 1 )
-
- break
-
- end
-
- end
-
- end
-
-
-
- %随机产生填充图像,这就是填充痘印部分的图像
-
- y = zeros(3,3,3);
-
- y( :,:,1 ) = randi([r(1)-5,r(1)+5],[3,3]);%取像素值-5到像素值+5的范围的随机数组成一个3X3的矩阵
-
- y( :,:,2 ) = randi([r(2)-5,r(2)+5],[3,3]);
-
- y( :,:,3 ) = randi([r(3)-5,r(3)+5],[3 3]);
-
-
-
- %类型转换
-
- y = double(y);
-
-
-
- %对于三个通道分别进行处理,用采样得到的像素点取代原来的像素点
-
- for i = 2:3:M-1
-
- for j = 2:3:N-1
-
- f1( i-1:i+1,j-1:j+1 ) = mask( i-1:i+1,j-1:j+1 ).* y( :,:,1 );
-
- f2( i-1:i+1,j-1:j+1 ) = mask( i-1:i+1,j-1:j+1 ).* y( :,:,2 );
-
- f3( i-1:i+1,j-1:j+1 ) = mask( i-1:i+1,j-1:j+1 ).* y( :,:,3 );
-
- end
-
- end
-
-
-
- %将三个通道连接在一起
-
- f = cat( 3,f1,f2,f3 );
-
-
-
- %类型转换
-
- f = uint8( f );
-
-
-
- %得到处理后图像,居然可以直接加减,那还学什么运算函数
-
- a = g2 - x;
-
- f = f + a;
-
- g2 = f;
-
- end
-
-
-
- %图像滤波处理,将图像进行高斯滤波处理,平滑
-
- f = double(f);
-
- b = double( imguidedfilter( uint8( f ) ) ) - f + 128 ;
-
- t = imfilter( b, fspecial( 'gaussian',[5 5]) );
-
- f = ( f*50 + ( f+2*t-256 )*50 )/100;
-
- f = uint8(f);
-
-
-
- %显示图像
-
- subplot( 1,2,1 ),imshow( g ),title('原图');
-
- subplot( 1,2,2 ),imshow( f ),title('填充处理后图像');
结果分析:因为祛痘区域的像素是根据区域左上角第一个像素+5-5这个范围,进行随机填充的。可以看到,如果痘痘长到脸部死角的话,填充效果是很突兀的,就算是平坦区域,人脸和填充区域也没有很好的连接,填充区域比较突兀,尽管进行了高斯滤波,效果还是一般,但是对于没有高光的人脸,祛痘效果应该不错。
实质上就是纹理的一个提取过程,首先是分离三通道,随便选择一个通道进行处理,然后将这通道取反,再用高斯滤波对负片进行滤波处理,去除一些噪点,最后就是按照比例将正片和负片进行叠加,最后归一化之后,就得到了比较理想的素描效果。
- I = imread('person11.jpg');
-
- [height,width,~]=size(I);%获取图像矩阵的大小
-
- N=zeros(height,width);%创建两个全零矩阵
-
- G=zeros(height,width);
-
- %分离三通道
-
- rc = I(:,:,1);
-
- gc = I(:,:,2);
-
- bc = I(:,:,3);
-
- %选择一个通道进行处理
-
- channel = gc;
-
- out=zeros(height,width);
-
- spec=zeros(height,width,3);
-
- %颜色取反
-
- for i=1:height
-
- for j=1:width
-
- N(i,j)=uint8(255-channel(i,j));
-
- end
-
- end
-
- %高斯模糊
-
- gausize = 10;%滤波器大小,越大越模糊
-
- gausigma = 12; %越大越模糊
-
- GH = fspecial('gaussian', gausize, gausigma);%构建高斯滤波模板
-
- G = imfilter(N, GH);%滤波处理
-
- for i=1:height
-
- for j=1:width
-
- b=double(G(i,j));
-
- a=double(channel(i,j));
-
- temp=a+a*b/(256-b);%将滤波后的负片和正片相叠加
-
- out(i,j)=uint8(min(temp,255));
-
- end
-
- end
-
-
-
- %模糊程度越高,得到的素描结果越清晰,框架纹理颜色越深
-
- figure
-
- subplot(121),imshow(out/255);%归一化处理并显示图像
-
- subplot(122),imshow(I);
效果分析:图片越清晰,效果越好,因为这个处理主要是对纹理进行提取,进而实现了类似素描的效果。
定义一个矩形窗口,用这个窗口划过图像的每一个像素点(三通道分别处理),计算矩形内每一个像素点的灰度值,取这个区域里面最大的像素值作为当前像素点的值,这样就实现了简单的油画效果。
- A=imread('person4.jpg');
-
- % 油画效果
-
- %创建12X12的窗口矩阵,窗口越大,丢失的信息就越多,效果就越明显
-
- m=12;
-
- n=12;
-
- Image=uint8(zeros([size(A,1)-m,size(A,2)-n,3]));
-
- figure
-
- imshow(Image);
-
- %计算每个RGB值的直方图
-
- for v=1:3
-
- for i=1:size(A,1)-m
-
- for j=1:size(A,2)-n
-
- mymask=A(i:i+m-1,j:j+n-1,v);
-
- h=zeros(1,256);
-
- for x=1:(m*n)
-
- h(mymask(x)+1)=h(mymask(x)+1)+1;
-
- end
-
- [maxvalue,pos]=max(h);%记录最大值和最大值对应的位置
-
- Image(i,j,v)=pos-1;%将最大值赋值给当前的像素
-
- end
-
- end
-
- end
-
- figure
-
- subplot(121),imshow(Image);
-
- subplot(122),imshow(A);
效果分析:窗口越大,效果越明显,但是由于取的是最大值,所以有些地方突变很突兀,效果不是很理想,但基本实现了油画效果。
首先创建一个处理的窗口,可以自定义大小,然后用这个窗口滑动图像的每一个像素,随机取这个窗口的某个像素,赋值给当前滑过的像素,直至遍历所有像素,毛玻璃效果就完成了。
- A=imread('buiding1.jpg');%读入图像
-
- %定义一个6X7的矩形窗口
-
- m=6;
-
- n=7;
-
- Image=uint8(zeros([size(A,1)-m,size(A,2)-n,3]));%存储窗口处理后的图像
-
- for i=1:size(A,1)-m
-
- for j=1:size(A,2)-n
-
- mymask=A(i:i+m-1,j:j+n-1,:);%将图像对应像素值赋值给窗口
-
- x2=ceil(rand(1)*m);%从窗口像素随机挑选一个像素,因为坐标必须为正整数,所以用ceil四舍五入为整数
-
- y2=ceil(rand(1)*n);
-
- Image(i,j,:)=mymask(x2,y2,:);%将选出的像素值赋值给当前的像素
-
- end
-
- end
-
- imshow(Image);%显示图像
这个效果的实现比较简单,实质上就是提取图像的边界,然后突出边界。具体步骤就是对灰度图进行Sobel滤波,提取边界,然后再进行灰度转化。
- img=imread('building2.jpg');%读入图像
-
- f0=rgb2gray(img);%改为灰度图像
-
- f1=im2double(f1);
-
- h2=fspecial('sobel'); %进行sobel滤波
-
- g3=filter2(h2,f1,'same');
-
- K=mat2gray(g3); %将矩阵转换成灰度图
-
- figure
-
- imshow(K);
效果分析:浮雕效果取决于边界提取的效果,所以灰度值变化大的图像,处理效果就很好,所以这个效果对建筑物等图片处理效果还可以,就贴了一张处理建筑物的图片。
实质上就是矩阵的旋转,旋转轴是图像的中心,让图像的每一个坐标经过旋转公式计算出旋转后的坐标,但是计算出来的坐标是浮点数,坐标必须是正整数,所以必须取整,但是取整后,可能有些坐标上像素值会出现重叠,有些坐标上可能出现没有像素的情况,所以用变换后的坐标来确定变换前的坐标,然后用变换前坐标附近的像素点进行简单的线性插值,算出变换后坐标的像素值,遍历完图像之后,就得到了旋转后的图像。
- %图像旋转
-
- I = imread('person10.jpg');%读入图像
-
- I = im2double(I);
-
- I2=imrotate(I,-22);
-
- figure
-
- subplot(121),imshow(I2),title('顺时针旋转22度');%调用函数实现
-
-
-
- %自己写代码实现
-
- %旋转22度
-
- a = 22/180*pi;%角度转换成弧度
-
- Rotate = [cos(a) -sin(a);sin(a) cos(a)];%旋转变换矩阵
-
- [m,n,~] = size(I);%矩阵大小
-
- center = [m,n]/2;%旋转轴中心
-
-
-
- % 计算显示完整图像需要的画布大小
-
- hh = floor(m*sin(a)+n*cos(a))+1;
-
- ww = floor(m*cos(a)+n*sin(a))+1;
-
- c2 = [hh; ww] / 2;%旋转之后的轴中心
-
- res = zeros(size(I));%创建存储结果的全零矩阵
-
- for k=1:3%三个通道
-
- for i=1:hh
-
- for j=1:ww
-
- R = Rotate*([i;j]-c2)+center;%变换公式
-
- R1 = floor(R);%向负无穷舍入
-
- ab = R-R1;%线性插值算法
-
- a = ab(1);
-
- b = ab(2);
-
- m1 = R1(1);
-
- n1 = R1(2);
-
- if (R(1) >= 1 && R(1) <= m && R(2) >= 1 && R(2) <= n)
-
- res(i, j, k) =(1-a)*(1-b)*I(m1,n1, k) + a*(1-b)*I(m1+1, n1, k)...
-
- + (1-a)*b*I(m1,n1+1,k)+ a*b*I(m1+1,n1+1, k);%简单线性插值
-
-
-
- end
-
- end
-
- end
-
- end
-
- subplot(122),imshow(res),title('顺时针旋转变化22');%显示图像
对于自编程的函数实现旋转,存在图片的拉伸,和图片显示不完整等问题,算法实现还是有一定问题。
利用交互式函数提取裁剪的区域,然后根据返回的参数,显示裁剪后的图像。
- %交互式裁剪图像
-
- I =imread(‘person.jpg’);%读取图像
-
- [J,rect] = imcrop(I);调用函数交互式裁剪图像,J返回的是裁剪的区域,rect返回的是指定裁剪区域的四元素位置向量。
-
- I2 = imcrop(I,rect);%根据返回的位置向量裁剪图像并返回
-
- imshow(I2);%显示裁剪的图像
效果分析:这个就是实现了交互式裁剪。
利用matlab函数fspecial,通过设置运动位移参数和位移角度参数,得到运动仿真滤波器,然后利用滤波器进行图像滤波,得到运动模糊后的图像。
- %运动模糊
-
- I = imread('buiding1.jpg');%读入图像
-
- len=20;%设置运动位移为20个像素
-
- theta=50;%设置运动角度为45度
-
- psf = fspecial('motion',len,theta);%建立二维运动仿真滤波器psf
-
- j = imfilter(I,psf,'circular','conv');%用滤波器产生退化图像
-
- figure
-
- subplot(121),imshow(j),title('运动模糊');%显示运动模糊图像
-
- subplot(122),imshow(I),title('原图');%显示原图像
图像的翻转实质上就是矩阵上元素序列的反转,flip这个函数,如果里面只有需要翻转的图像I的话,默认翻转每一列每一行的元素序列,如果在后面加上维度1,就是翻转每一列的元素序列,就实现了上下翻转的效果,如果在后面加上维度2,就是翻转每一行的元素序列,就实现了左右翻转的效果。
- %图像翻转
-
- I = imread('person.jpg');
-
- res = flip(I,2);%左右翻转
-
- res1 = flip(I,1);%上下翻转
-
- figure,
-
- subplot(131),imshow(I),title('原图');
-
- subplot(132),imshow(res),title('左右翻转');
-
- subplot(133),imshow(res1),title('上下翻转');
这个功能主要是依赖于matlab的交互式选取多边形感兴趣的区域,选取之后,对图像未选取的部分赋值为0,具体操作是:因为roipoly函数返回了一个二进制图像,选中的区域元素值为1,未选中的部分元素值为0,所以对图像三个通道分别乘以这个二进制图像,然后用cat函数实现三通道的合成,就得到了扣取的图像。
- %抠除背景
-
- %基于提取感兴趣区域扣除背景
-
- I = app.updateImg;
-
- g2=I;
-
- %进行交互选择处理区域
-
- mask = roipoly( g2 );%roipoly函数--指定多边形感兴趣区域,将蒙版作为二进制图像返回,所以下面的乘法就可以得到原图像被提取的区域,因为0的部分乘以任何像素值都为0,1乘以原像
-
- %素还是等于原像素
-
- app.mask=mask;
-
- x1 = immultiply( mask,g2( :,:,1 ) );
-
- x2 = immultiply( mask,g2( :,:,2 ) );
-
- x3 = immultiply( mask,g2( :,:,3 ) );
-
- x = cat( 3,x1,x2,x3 );%将x1和x2和x3沿第三维度串联起来,这里相当于三通道合并
-
- app.Bg=x;
-
- imshow(x,'parent',app.UIAxes),title('扣取的图像');
效果分析:就是这种交互式的,想要抠出比较完美的对象,很繁琐,而且只能拉直线,扣取对象的边缘就会很生硬,并且很多多余部分。
这个功能主要是基于上一个去除背景的功能上实现的,因为这个功能实质上是图像的叠加,所以如果背景图小于扣取的图像,就会报错,所以我做了一个判断,如果选取的背景图片过于小,就弹出提示框,那么就只能重新选取背景图片。然后用图像的大小来裁剪背景图片,以便后面图片的加法,最后用背景图减去扣取的图像(利用乘法,让扣取的人像部分像素值为0),再利用图像的叠加,就得到了一个背景和前景的合成图。
- %创建文件选择对话框选择指定图像
-
- [file,path]=uigetfile('*.png');
-
- bg=imread(fullfile(path,file));%读取图像
-
- [M,N,~]=size(bg);
-
- [A,B,~]=size(app.Bg);
-
- %比较背景图和人像图的矩阵大小
-
- if(M<A|N<B)
-
- %弹出警告对话框
-
- msgbox("当前选择背景图过小,请重新选择!","警告",'non-modal');
-
- end
-
- %基于提取感兴趣区域扣除背景
-
- I = app.Bg;
-
- [M,N,~] = size(I);%获取图片的大小
-
- %按照图像裁剪背景图
-
- bg = imcrop(bg,[0,0,N,M]);
-
- mask=app.mask;
-
- % figure
-
- % imshow(bg),title('裁剪的图像');
-
- %用裁剪的图像减去扣取的图像
-
- b1 = immultiply( ~mask,bg( :,:,1 ) );
-
- b2 = immultiply( ~mask,bg( :,:,2 ) );
-
- b3 = immultiply( ~mask,bg( :,:,3 ) );
-
- b =cat(3,b1,b2,b3);
-
- %处理之后的背景图加上扣取的图像
-
- app.updateImg=b+I;
-
- imshow(b+I,'Parent',app.UIAxes);
效果分析:因为在处理背景和人像的叠加的时候,我是根据人像图片的大小来裁剪的背景图,所以背景图总是无法显示完整,只能显示左上角的部分。而且背景图必须比人像的图片大,不然裁剪的时候,会出现数组越界,报错。
双边滤波进行磨皮效果的处理,有很好的保边效果
增大眼睛,主要是基于眼睛定位,然后进行局部图像的放大
缩小鼻翼,跟增大眼睛的原理大同小异,首先定位鼻子,然后进行局部图像缩小
瘦脸,虽然实现效果很差,但是根据公式写出来的算法,实现还是很成功的,可能就是需要对圆形区域每一个像素都进行一个连续的液化平移,但是没时间去实现了。
一开始学习这门课程,拿到教材之后,翻开书,我傻眼了,都是汉字,但是我就是看不懂。每次上完课,想拿出教材看看,看了几页就放弃了。到了这门课程后期,到形态学这一部分,我感觉可以用这些算法实现好多功能,所以在做大项目的时候,我又翻开了书,书上的代码和算法好像没有那么陌生了,有些算法甚至可以自己单独写出来了。
实现简易版的美图秀秀,我觉得首先得解决人脸定位问题,于是我开始查看相关算法,搜罗了几天,我总结了一下,主要有四种方法:最大连通域(默认最大连通域是人脸)、肤色检测(有很多颜色模型,教材上都有肤色的阈值范围,我采用的YCbCr颜色模型)、V-J算法(基于haar-like的特征提取)、还有就是深度学习(没涉及)。然后我将前三种算法,都找了20张比较清晰的人像样本进行测试,比较他们的优劣。毫无疑问,V-J算法效果远胜与其他两种,所以我就采用了matlab自带的人脸检测器(基于V-J算法)。然后我去看V-J算法,看了很多博文,下课之后,我又再次打开了讲解V-J算法的博文,这次搞懂了百分之八九十。
然后就是人脸磨皮,我一开始只想到了在空间域和频域的各种滤波,于是自己实现了一下,效果很差,五官几乎被磨平了。不知道哪次偶然翻开教材,发现教材上还有一种平滑滤波--双边滤波,不仅考虑了像素的位置,还考虑了像素的值,位置近但是灰度值差异大,这个算法就会降低这种像素值的权重,所以就实现了平滑保边的效果。于是我参照教材上的代码写了一遍,当然教材上是处理的灰度图,我就只能将彩色图的三通道分离,分别进行双边滤波之后,再次合成,虽然这个算法计算量很大,处理速度很慢,但是效果比其他的滤波都好。
再次就是眼部增大的功能,这个我一开始一直没有头绪,虽然matlab那个人脸定位可以定位眼部,但是有时候也不准确,比如定位嘴巴的时候,就会连眼睛也一起识别出来。我想,眼睛增大,实质上不就是图像的局部增大嘛?只要确定增大的范围,再进行插值,就可以实现了,于是我开始利用matlab进行单只眼睛的定位,很好笑的是,我明明只让它定位左眼,但是它返回了两只眼睛的位置向量,然后我就两只眼睛分别进行图像增大,插值我用的是最邻近插值,没有用双线性插值,这样计算量小一些,而且效果也不差。
缩小鼻翼,这个功能是在我睡觉的时候,突然想实现这么一个功能,我一开始想的是等比例缩小鼻子区域,结果实现之后,发现效果很差,鼻子都歪了,因为那个算法本身也是基于像素的间隔采样,然后线性插值,实现的图像的缩小。就会把鼻子区域缩小到原鼻子区域的左上角。后来,我发现放大眼睛的功能不就是这样的效果吗?只要把放大变成缩小就可以了,所以我就改了几行代码,效果很快就出来了。
再次,就是瘦脸的效果,我一开始完全没有头绪,搜索出来的算法全部都是python语言的,然后博主的讲解,我也没搞懂。这个契机又是专业同学的那次算法讲解,液化算法,虽然实质上就是像素的平移,但是我一直没找到逆变换的公式,因此这个算法我搁置了好久,每天都在全网搜索。最后,我终于看到一篇讲解液化算法的文章,博主讲的很清晰,虽然没有给代码,但是有了逆变换公式,我尝试自己写代码,其实也比较简单,先遍历选取的圆形区域的像素,然后再确定平移之后的圆心,就可以根据逆变换公式算出变换之后对应位置,所对应的变换之前的像素值,然后进行双线性插值,这个地方让我想起大二学的《计算机图形学》,以前还不知道这个算法有什么用,没想到这个学期排上了用场。虽然圆形区域的像素是如愿以偿的平移了,但是人脸像素在移动的时候,出现了很大的割裂。所以,尽管这个功能我花了很多时间,但是这个功能算是失败了。
除了每个功能的实现,还要考虑按钮之间的关系,怎么设置变量才能让整个软件流畅的运行,而不是随便一点就报错。我用了几个属性来保存变换之后的图像,这样处理的图片,就能接着处理了,但是在滤镜部分,那些效果不可能进行叠加,所以我设置了一个保存按钮,用于用户确认想要这个效果之后,进行效果图的保存,然后再进行后续处理,这些让我意识到,一个图像处理软件需要考虑的问题不仅仅每个功能模块的实现,还要根据用户的习惯,将某些功能模块关联在一起实现,而像滤镜类似的功能模块,则需要独立去实现。
参考博客:
(37条消息) 【数字图像处理】MATLAB实现图像旋转_Y_CanFly的博客-CSDN博客_图像旋转matlab
(37条消息) 积分图算法及代码实现(笔记)_molihong28的博客-CSDN博客
(37条消息) 数据挖掘领域十大经典算法之—AdaBoost算法(超详细附代码)_fuqiuai的博客-CSDN博客_adaboost算法
(37条消息) [Matlab]简单的人脸祛痘_ZJU_fish1996的博客-CSDN博客
(37条消息) 图像处理算法之瘦脸及放大眼睛_grafx的博客-CSDN博客_主流大眼瘦脸 算法
(37条消息) Vision.CascadeObjectDetector-VJ算法学习_雪山飞狐W的博客-CSDN博客_vision.cascadeobjectdetector
(37条消息) Viola-Jones人脸检测_通信程序猿的博客-CSDN博客
图像变形算法:实现Photoshop液化工具箱中向前变形工具 - xiaotie - 博客园 (cnblogs.com)
(37条消息) MATLAB App Designer入门实战(一)_slandarer的博客-CSDN博客
(37条消息) MATLAB 图像处理 简单人脸检测(详细,你上你也行)_Kakaluotuo的博客-CSDN博客_matlab人脸检测
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。