原文:人像美妆---妆容迁移算法研究(Makeup transfer)

对于人像美妆算法,现在的美妆相机、玩美彩妆之类的app已经做的比较成熟了,但是具体算法,基本网络上是杳无可查,今天本人介绍一种自动的人像美妆算法----(Makeup Transfer)妆容迁移

妆容迁移相关的论文不多,有如下几篇:

1.Example-Based cosmetic transfer

2.Makeup Transfer using Multi-example

3.A new digtial face makeup method

4.An automatic framework for example vased virtual makeup

本人主要介绍第一篇《Example-Based cosmetic transfer》,论文效果图如下:

注意:A和A*是两张输入,B是原始图像,B*是根据A*迁移过来的妆容效果图;

本文的算法流程如下:

1,Face warp

若要讲A*的妆容迁移到B上,文中有几个条件:

①背景单一;

②肤色相近;

这两个条件也是为了最后的效果更加自然;

首先,Face warp的过程需要有人脸特征点,因此,这一步之前需要进行人脸检测和点位对齐,拿到A*和B的特征点;

然后,根据变形算法讲A*和B的特征点对齐,从而将A*变形到与B一致;

变形算法有以下几种(个人总结):

①最小二乘MLS变形算法:Image Deformation Using Moving Least Squares.

详细介绍参考博客:http://blog.csdn.net/hjimce/article/details/46550001

②基于线的变形算法:As-Rigid-As-Possible Shape Manipulation.

详细介绍参考博客:http://blog.csdn.net/hjimce/article/details/45766321

③三角网格仿射变换

给出这一步的效果图如下:

左边是原图B(为避免 侵权,眼睛做了处理),中间是妆容图A*,右边是Face warp之后的效果图(我这里采用的是三角网格变形);

2,Cosmetic Map计算

文中介绍的重点也就是这一步,计算Cosmetic Map,即CP,其实算法很简单,公式如下:

Cp = ap / ap*

ap:妆容图像A*对应的原图A

ap*:妆容图像A*

就这么一个简单的公式,就可以化腐朽为神奇。

文中所给CP效果图如下:

3,Makeup transfer

得到了CP之后,我们就可以来进行妆容迁移了,具体算法如下:

4,Others

实际上论文中还介绍一些其他内容,这里我没有写出,因为我的重点是妆容迁移,所以就主要提取了这块内容。

论文中实际上还进行了Freckle remove雀斑去除,眉毛眼睛纹理细节提取等等,如下所示:

这些内容,我这里不关心,实际上,就是为了让最后的效果更加自然更加逼真;

以上整个过程就是这篇论文的核心算法;

现在,好东东才刚刚开始:

本人对这篇论文提出一下几个问题,实际上也是应用中的缺陷:

①,论文要求三个输入(A, A*, B),一个输出B*

这一点,实际应用中就有很大限制,一般而言,我们能拿到A*,也就是好看的妆容效果图,然后想对自己的照片B进行化妆,这个逻辑中是没有未化妆的原图A的。

②,论文中要求肤色相近,背景单一

这一点,普适性太低,很难应用;

③,按照论文的逻辑,嘴巴区域是闭合的,无法适应于各种大笑等开口的情况,或者是效果太差;

介于以上三点,本人对算法进行了改进:

①,根据A*,对A进行估计,估算得到A,这样就只要求用户输入一张好看的效果图,即可对自己的自拍照等进行妆容迁移了;

②,根据人脸特征点,获取A*中的肤色特征,构建精准的人脸Mask,去除背景,这样就避免了背景的影响,同时,进行肤色转换,将A*中的肤色转换到B*中去,从而避免肤色差异过大造成的影响;

③获取A*中的唇色特征,对B进行唇色转换,即将A*的肤色和唇色迁移到B*中去,从而使用于各种大笑等开口场景;

根据上述三点,本人改进算法,得到如下的结果:

原图B

三个目标妆容B*

上述三个妆容B*分别对应的效果图如下:

在给一组测试图:

以上效果本人做了化妆程度自适应,所以没有出现很饱满的艳妆,这样是为了看起来更自然一点。

注意:本人使用的测试图来自美颜相机和互联网,若有侵权敬请告知。

本人提供简单的代码调用如下:

      private void pictureBox4_Click(object sender, EventArgs e)
{
if (pictureBox1.Image != null)
{
Graphics g = Graphics.FromImage(curBitmap);
int[] eyePoints = {
173, 370, 177, 441, 191, 509, 212, 577, 236, 640,
267, 698, 303, 748, 347, 793, 399, 826, 465, 837,
526, 825, 575, 792, 611, 748, 643, 699, 671, 643,
695, 578, 714, 507, 727, 435, 728, 364, 214, 316,
245, 284, 285, 273, 328, 274, 370, 281, 402, 308,
363, 309, 325, 304, 287, 302, 251, 307, 513, 307,
544, 282, 583, 275, 623, 274, 660, 284, 688, 313,
654, 306, 620, 302, 585, 304, 549, 309, 269, 390,
282, 373, 300, 364, 323, 361, 347, 366, 365, 380,
378, 401, 360, 406, 342, 410, 321, 412, 300, 408,
283, 401, 533, 399, 544, 378, 562, 365, 585, 359,
607, 362, 625, 371, 638, 386, 624, 398, 608, 406,
588, 410, 567, 408, 550, 404, 424, 394, 424, 453,
417, 512, 386, 542, 398, 580, 446, 588, 480, 588,
528, 576, 536, 539, 506, 511, 494, 452, 490, 393,
363, 653, 394, 643, 429, 637, 462, 642, 495, 636,
527, 643, 557, 654, 535, 687, 506, 713, 461, 726,
415, 715, 384, 688, 373, 656, 417, 657, 462, 661,
504, 657, 546, 657, 505, 676, 460, 686, 414, 676,
322, 389, 586, 387, 457, 392, 461, 502, 463, 554,
463, 588 }; eyePoints[2 * 50 + 1] -= 2;
eyePoints[2 * 49 + 1] -= 3;
eyePoints[2 * 48 + 1] -= 4;
eyePoints[2 * 47 + 1] -= 3;
eyePoints[2 * 46 + 1] -= 2; eyePoints[2 * 52 + 1] -= 1;
eyePoints[2 * 61 + 1] -= 2;
eyePoints[2 * 60 + 1] -= 3;
eyePoints[2 * 59 + 1] -= 2;
eyePoints[2 * 58 + 1] -= 1;
for (int i = 0; i < 101; i++)
{
g.DrawRectangle(new Pen(Color.Red, 1), new Rectangle(eyePoints[2 * i] - 1, eyePoints[2 * i + 1] - 1, 2, 2));
}
g.Dispose();
DateTime start = DateTime.Now;
curBitmap = ip.SoftSkin(srcBitmap, new Bitmap(startPath + "\\MakeUp\\MAP.png"), null, skinRatio, 30); curBitmap = ip.MKMakeupTransfer(curBitmap, new Bitmap(Application.StartupPath + "\\M3.JPG"), curFacePoints, eyePoints);
DateTime end = DateTime.Now;
label1.Text = "TC: " + (end - start).ToString();
pictureBox1.Image = curBitmap;
}
}
 [DllImport("TestDemo_C.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.None, ExactSpelling = true)]
        private static extern int IN_Pic_MakeupTransfer(byte* srcData, int width, int height, int stride, int[] srcFacePointsAll, byte* maskData, int mWidth, int mHeight, int mStride, int[] mKeyPointsAll);
        public Bitmap MKMakeupTransfer(Bitmap src, Bitmap mask, int[] srcFacePointsAll, int[] mskFacePointsAll)
        {
            Bitmap a = new Bitmap(src);
            int w = a.Width;
            int h = a.Height;
            BitmapData srcData = a.LockBits(new Rectangle(0, 0, a.Width, a.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            BitmapData mskData = mask.LockBits(new Rectangle(0, 0, mask.Width, mask.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            IN_Pic_MakeupTransfer((byte*)srcData.Scan0, w, h, srcData.Stride, srcFacePointsAll,(byte*)mskData.Scan0, mask.Width, mask.Height, mskData.Stride,  mskFacePointsAll);
            a.UnlockBits(srcData);
            mask.UnlockBits(mskData);
            return a;
        }

最后,给一个测试DEMO:点击打开链接

如果问题,请联系QQ:1358009172

人像美妆---妆容迁移算法研究(Makeup transfer)的更多相关文章

  1. 美图美妆由Try Try接手运营

    美图又把一个拖累营收的业务转让出去了. 美图的电商业务——美图美妆应用在向用户发布终止运营的公告后,宣布把业务交给了寺库旗下公司 Try Try 运营.Try Try 接手了美图美妆的所有管理运营权, ...

  2. MugLife静态照片变3D动画算法研究

    原文:MugLife静态照片变3D动画算法研究 MugLife app是一款可以将静态照片变成3D动画的手机应用,如下效果图所示: 大家可以看到,这个静态图具有了类3D的动画特效,是不是很好玩? 这种 ...

  3. 照片美妆---基于Haar特征的Adaboost级联人脸检测分类器

    原文:照片美妆---基于Haar特征的Adaboost级联人脸检测分类器 本文转载自张雨石http://blog.csdn.net/stdcoutzyx/article/details/3484223 ...

  4. 《A Survey on Transfer Learning》迁移学习研究综述 翻译

    迁移学习研究综述 Sinno Jialin Pan and Qiang Yang,Fellow, IEEE 摘要:   在许多机器学习和数据挖掘算法中,一个重要的假设就是目前的训练数据和将来的训练数据 ...

  5. python opencv 实现Reinhard颜色迁移算法

    Reinhard颜色迁移算法的过程很简单,流程如下,细节部分见原文,题目为color transfer between images: 将参考图片和目标图片转换到LAB空间下 得到参考图片和目标图片的 ...

  6. July-程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大经典原创系列集锦与总结

    程序员面试.算法研究.编程艺术.红黑树.数据挖掘5大经典原创系列集锦与总结 http://blog.csdn.net/v_july_v/article/details/6543438

  7. Akamai在内容分发网络中的算法研究(翻译总结)

    作者 | 钱坤 钱坤,腾讯后台开发工程师,从事领域为流媒体CDN相关,参与腾讯TVideo平台开发维护. 原文是<Algorithmic Nuggets in Content Delivery& ...

  8. 经典算法研究系列:二、Dijkstra 算法初探

    July   二零一一年一月 本文主要参考:算法导论 第二版.维基百科. 一.Dijkstra 算法的介绍 Dijkstra 算法,又叫迪科斯彻算法(Dijkstra),算法解决的是有向图中单个源点到 ...

  9. 静态频繁子图挖掘算法用于动态网络——gSpan算法研究

    摘要 随着信息技术的不断发展,人类可以很容易地收集和储存大量的数据,然而,如何在海量的数据中提取对用户有用的信息逐渐地成为巨大挑战.为了应对这种挑战,数据挖掘技术应运而生,成为了最近一段时期数据科学的 ...

随机推荐

  1. 常用JS验证函数总结

    JS验证Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/- ...

  2. [React Router v4] Conditionally Render a Route with the Switch Component

    We often want to render a Route conditionally within our application. In React Router v4, the Route ...

  3. [Angular] Create dynamic content with <tempalte>

    To create a dynamic template, we need to a entry component as a placeholder. Then we can use entry c ...

  4. C语言实现使用动态数组来构造栈结构

    我在面前一篇博客<C语言实现使用静态数组来构造栈结构>中使用了静态数组来模拟栈的操作.静态数组的大小是在代码中写死的.是存储在用户栈上面的,使用起来不灵活.在这篇博客中我会使用动态数组来构 ...

  5. CSS学习小结

    接触了B/S的东西之后才发现自己须要学习的东西太多了.html.xml.JavaScript.jquery.HTMLdom.VBScript.ajax.jquery.json等等技术都是须要我们一一研 ...

  6. UiwebView and html

    基础篇: NSURL介绍 http://blog.csdn.net/ysy441088327/article/details/7416759 网页执行js代码   复制代码 stringByEvalu ...

  7. DDoS ATTACK PROCESSING APPARATUS AND METHOD IN OPENFLOW SWITCH

    An OpenFlow switch in an OpenFlow environment includes an attack determination module to collect sta ...

  8. Sitecore

    Sitecore 功能最强大.最丰富的企业级 .NET 网站内容管理系统.包含的功能如下所述: 内容编辑和会话中的个性化 用于预览访客所看到的网站内容的体验浏览器 设备和地理位置 IP 检测(需要额外 ...

  9. 一起学Python:网络通信过程

    1. 2台电脑的网络 image 说明 如果两台电脑之间通过网线连接是可以直接通信的,但是需要提前设置好ip地址以及网络掩码 并且ip地址需要控制在同一网段内,例如 一台为192.168.1.1另一台 ...

  10. experiment : 在私有堆和默认进程堆中, 测试能分配的堆空间总和, 每次能分配的最大堆空间

    实验环境: Win7X64Sp1 + vs2008,  物理内存16GB. 实验结论: *  进程堆的最大Size并没有使用完剩余的物理内存    *  每次能分配的最大堆空间接近2M, 不管是私有堆 ...