• 图像处理之face morphing


    以前在论坛、微博经常看到一张脸,五官长得像A,脸型似乎又是B,觉得很有意思。

    比如像这张图片。这张图片应该是网友用Photoshop完成的,他们取了郭大爷的五官,放在金元帅的脸上,在把边缘处理平滑。

    而上面这张图片是另外一种效果,它不仅改变了五官,连脸型轮廓也一起改变了。这种技术称为face morphing,这篇随笔就聊一聊它吧。

    Morphing是指把一张照片变换成另一张照片,中间的变换过程如行云流水一般自然。

    Cross-Disolve

    最方便的方法是像素值叠加,第一幅照片的像素值乘以系数k,加上第二幅照片的对应像素值乘以系数(1-k)。这个方法尽管操作方便,但是效果并不好。首先,要把两幅照片的尺寸调整到一样大小,然后图像中主要内容的位置也应该保持一致,否则直接叠加会形成虚影。

    Feature-based Morphing

    Beier和Neely对这个方法做了改进,他们提出了一种基于特征的图像变换新方法。

    假设目标图像里有一条直线PQ表示施瓦辛格的鼻梁,在源图像里小布什的鼻梁是P'Q'。那么我们就可以得到一个函数对应关系P'(x,y) = f(P(x,y)), Q'(x,y) = f(Q(x,y)). 而图像里的其它任意点X则可以根据PQ和P'Q'的关系来推算。

    如果只用这一对特征线段来计算,那么在线段附近的区域可以很好的预测,稍远的区域就难以估计了。于是,可以选择多条特征线段,比如眼眶、发际线、下巴、领口等区域。多对线段和一对线段的方法类似,根据前面一对特征线段的方法,点X对每一对线段PiQi都计算得到一个点Xi,同时按照点X和线段PQ距离的远近得到一个权重值wi,距离越远权重越小。最后,X= Σwi*Xi。对于照片上的每一个像素X(x,y)都做这个运算,就得到了合成图像。

    Mesh-based Morphing

    另一种方法是基于网格的变换,它的思路就是在源图像和目标图像上标注若干对应的特征点,按照特征点把整张图像分割成若干块三角形区域。为保证五官在变换中的完整,五官和脸的轮廓应该多放置特征点,但是过多的特征点又会使运算速度降低。图像A的特征点数目和图像B特征点的数目是一致的,所以可以按照PC = α*PA + (1-α)*PB 的公式融合生成特征点在新图像中的位置。α∈[0,1]表示图像A对新图像的贡献率,α=1时新图像就是图像A。

    下一步,用inverse warping的方法,找出图像C的点在图像A和图像B的对应位置。用插值的方法求出在图像A和图像B的像素值。同样按照PC = α*PA + (1-α)*PB 的公式将两者混合。

    上面三张照片是α=0.25, 0.56, 0.75时的效果。

    比较

    Cross-Disolve方法最简单,但是效果不好。Feature-based Morphing方法直观,容易理解,实现也比较方便,但是如果取得特征线段不好,会有“意外效果”出现。Mesh-based Morphing方法人工选择特征点的过程略微复杂,但是效果比较好。

    附Matlab代码:

    A = imread('imA.jpg');
    height = size(A,1);
    width = size(A,2);

    imshow(A);
    [xA,yA] = ginput(64);
    xA = [xA;1;width;width;1];
    yA = [yA;1;1;height;height];

    B = imread('C:HYHcodepicwb.jpg');
    imshow(B);
    [xB,yB] = ginput(64);
    xB = [xB;1;width;width;1];
    yB = [yB;1;1;height;height];

     

    alpha = 0.75;

    C = zeros(height,width,3);
    xC = alpha*xA + (1-alpha)*xB;
    yC = alpha*yA + (1-alpha)*yB;
    triC = delaunay(xC,yC);
    ntri = size(triC,1);


    xCA = zeros(height,width);
    yCA = zeros(height,width);
    xCB = zeros(height,width);
    yCB = zeros(height,width);


    [X,Y] = meshgrid(1:width,1:height);

    for k = 1:ntri
    [w1,w2,w3,r] = inTri(X, Y, xC(triC(k,1)), yC(triC(k,1)), xC(triC(k,2)), yC(triC(k,2)), xC(triC(k,3)), yC(triC(k,3)));
    w1(~r)=0;
    w2(~r)=0;
    w3(~r)=0;
    xCA = xCA + w1.*xA(triC(k,1)) + w2.*xA(triC(k,2)) + w3.*xA(triC(k,3));
    yCA = yCA + w1.*yA(triC(k,1)) + w2.*yA(triC(k,2)) + w3.*yA(triC(k,3));
    xCB = xCB + w1.*xB(triC(k,1)) + w2.*xB(triC(k,2)) + w3.*xB(triC(k,3));
    yCB = yCB + w1.*yB(triC(k,1)) + w2.*yB(triC(k,2)) + w3.*yB(triC(k,3));
    end

    VCA(:,:,1) = interp2(X,Y,double(A(:,:,1)),xCA,yCA);
    VCA(:,:,2) = interp2(X,Y,double(A(:,:,2)),xCA,yCA);
    VCA(:,:,3) = interp2(X,Y,double(A(:,:,3)),xCA,yCA);

    VCB(:,:,1) = interp2(X,Y,double(B(:,:,1)),xCB,yCB);
    VCB(:,:,2) = interp2(X,Y,double(B(:,:,2)),xCB,yCB);
    VCB(:,:,3) = interp2(X,Y,double(B(:,:,3)),xCB,yCB);

    C = alpha*VCA + (1-alpha)*VCB;
    imshow(uint8(C));

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % inTri checks whether input points (vx, vy) are in a triangle whose
    % vertices are (v0x, v0y), (v1x, v1y) and (v2x, v2y) and returns the linear
    % combination weight, i.e., vx = w1*v0x + w2*v1x + w3*v2x and
    % vy = w1*v0y + w2*v1y + w3*v2y. If a point is in the triangle, the
    % corresponding r will be 1 and otherwise 0.
    %
    % This function accepts multiple point inputs, e.g., for two points (1,2),
    % (20,30), vx = (1, 20) and vy = (2, 30). In this case, w1, w2, w3 and r will
    % be vectors. The function only accepts the vertices of one triangle.
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function [w1,w2,w3,r] = inTri(vx, vy, v0x, v0y, v1x, v1y, v2x, v2y)
    v0x = repmat(v0x, size(vx,1), size(vx,2));
    v0y = repmat(v0y, size(vx,1), size(vx,2));
    v1x = repmat(v1x, size(vx,1), size(vx,2));
    v1y = repmat(v1y, size(vx,1), size(vx,2));
    v2x = repmat(v2x, size(vx,1), size(vx,2));
    v2y = repmat(v2y, size(vx,1), size(vx,2));
    w1 = ((vx-v2x).*(v1y-v2y) - (vy-v2y).*(v1x-v2x))./...
    ((v0x-v2x).*(v1y-v2y) - (v0y-v2y).*(v1x-v2x)+eps);
    w2 = ((vx-v2x).*(v0y-v2y) - (vy-v2y).*(v0x-v2x))./...
    ((v1x-v2x).*(v0y-v2y) - (v1y-v2y).*(v0x-v2x)+eps);
    w3 = 1 - w1 - w2;
    r = (w1>=0) & (w2>=0) & (w3>=0) & (w1<=1) & (w2<=1) & (w3<=1);
    end

  • 相关阅读:
    批量SSH操作工具---OmniTTY安装
    CentOS6.6修改主机名和网络信息
    浪潮服务器通过ipmitool获取mac地址
    linux批量执行工具omnitty使用方法
    操作系统下查看HBA卡信息wwn的方法
    Linux下multipath多路径配置
    IPMITOOL 配置BMC用户设置
    第五讲 对于耦合的认识 target/action设计模式 delegate设计模式 手势识别器
    UI第四讲.事件处理(按钮点击切换视图,触摸事件)
    UI第三讲.自定义视图 视图控制器 检测屏幕旋转
  • 原文地址:https://www.cnblogs.com/naive/p/3572976.html
Copyright © 2020-2023  润新知