zoukankan      html  css  js  c++  java
  • 图像处理之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

  • 相关阅读:
    JS BOM对象 History对象 Location对象
    JS 字符串对象 数组对象 函数对象 函数作用域
    JS 引入方式 基本数据类型 运算符 控制语句 循环 异常
    Pycharm Html CSS JS 快捷方式创建元素
    CSS 内外边距 float positio属性
    CSS 颜色 字体 背景 文本 边框 列表 display属性
    【Android】RxJava的使用(三)转换——map、flatMap
    【Android】RxJava的使用(二)Action
    【Android】RxJava的使用(一)基本用法
    【Android】Retrofit 2.0 的使用
  • 原文地址:https://www.cnblogs.com/naive/p/3572976.html
Copyright © 2011-2022 走看看