自己动手,实现“你的名字”滤镜

简介: 我喜欢《你的名字》这个故事,前一段时间在微信上使用过它的滤镜,实现的效果很惊艳,应该类似于下面的这些结果        这三幅图应该都是手机版本制作的,它们一个比较显著的特点是分辨率比较相似。
    我喜欢《你的名字》这个故事,前一段时间在微信上使用过它的滤镜,实现的效果很惊艳,应该类似于下面的这些结果
     img_efed717f852aa1c0fc907e3ae2eada6b.jpe img_13879b30ab7726ffe092cd6ff897f222.jpe img_8513ff9cc850638ee993aef0be482c1d.jpe
    这三幅图应该都是手机版本制作的,它们一个比较显著的特点是分辨率比较相似。如何实现类似的效果了?(注意后面两图天上的云是相同的)
    首先我想到的是Prisma的实现方法,这种实现方法,最后得到的是纹理的转换,效果应该说非常好,但是依赖于深度学习,目前这个方面掌握的不是很明确。而且显然上面三图不是纹理转换。
img_6e1326ccde621bbcd3da2320b86dda6f.jpe
    继续寻找,主要参考《 实现<你的名字>同款滤镜,python+opencv》等相关资料。
一、问题分析
       img_ba96cc2aa368805e615958d3d20b01f1.jpe
       对于这样一副图片,如果想变成《你的名字》这种效果,需要做以下事情
    (一)背景(天空)分割,替换后再融合
    在自然界的图片中,很难出现动漫中大多大多的云彩。首先需要将 背景(天空)分割出来,替换成动漫的天空,并且在很好地融合回去。
    需要实现的技术:1. 背景(天空)分割;2.再融合。需要准备的材料:1.大块的动漫云图
    (二)前景色调转换
    为了实现漫画中具有卡通意味的前景色调,需要对前面切割下来的前景图片进行色调转换。
    需要实现的技术:3.LUT和色块制作
  (三)程序框架
    需要实现的技术:基于GOMfcTemplate2,实现图像的输入输出、滤镜操作的参数选择等基础操作
二、材料准备
1.大朵的云。先找到一副自然界中的云。
img_4d48d9659d5e5bc8a7c816ecacc09642.jpe
再准备(制作)一幅动漫的云和一副星空的图片.
img_cf5ff159ba1fd6fe8f401851f65eede6.jpe
img_72b53b54306f12d4341b84916b26f3da.jpe
三、算法实验
1. 背景(天空)分割,采用材料中的方法,修改形态学部分
/************************************************************************/
/* 1.背景(天空)分割                                                                  */
/************************************************************************/
    cvtColor(matSrc,temp,COLOR_BGR2HSV);
    split(temp,planes);
    equalizeHist(planes[2],planes[2]);//对v通道进行equalizeHist
    merge(planes,temp);
    inRange(temp,Scalar(100,43,46),Scalar(124,255,255),temp);
    erode(temp,temp,Mat());//形态学变换,填补内部空洞
    dilate(temp,temp,Mat());
    imshow("原始图",matSrc);
对于这幅图来说,效果不错(右上角明显的是错误,而塔中间的一个很小的空洞,最后生成的效果应该可以忽略不计)
img_942b78ee55a9d03e4f81a1d10fa071ba.png

2.再融合
以此为mask,直接将云图拷贝过来(之前需要先做尺度变换,就是resize)  距离变换?
cvtColor(temp,mask,COLOR_BGR2GRAY);//将结果存入mask
resize(matCloud,matCloud,matSrc.size());
matCloud.copyTo(matSrc,mask);
img_0693ab2fdac1977323b4f6cbccb6c91f.jpe
这个时候看图片,还是有很多瑕疵的,特别是下方护栏的边缘的地方。但是对于这样的图片,其前景和背景的分割,主要部分的效果还是非常不错的;
所以采用seamlessclone,得到以下结果
//seamless clone
    Point center(matSrc.cols/2,matSrc.rows/2);
    Mat normal_clone;
    Mat mixed_clone;
    Mat monochrome_clone;
    seamlessClone(matCloud, matSrc, mask, center, normal_clone, NORMAL_CLONE);
    seamlessClone(matCloud, matSrc, mask, center, mixed_clone, MIXED_CLONE);
    seamlessClone(matCloud, matSrc, mask, center, monochrome_clone, MONOCHROME_TRANSFER);
NORMAL_CLONE
img_61ae63a17557770340239e0afb01aa19.jpe
MIXED_CLONE
img_be1eba8a4ffe85b85bcee2106b20ea39.jpe
MONOCHROME_TRANSFER
img_f1d415d1b7036df89e83a536d18f013d.jpe
相比较之下, MIXED_CLONE对原图纹理的保存更好一些,对有错误的地方的进行了很好地遮盖。
3.LUT和色卡制作
色调转换的意义,在于使得全图具有更多的“卡通”意味在厘米,毕竟我们想要实现的最终效果想要类似“你的名字”。那么什么是“动漫”的效果?这是一个见仁见智的问题。我找到一些做得较好的转换
巧用Photoshop滤镜将肖像照片变成卡通特效_天极软件 巧用Photoshop滤镜将肖像照片变成卡通特效_天极软件
103126cmxj9137zh5j7n1h.jpg 103130jjg4gy4upgiynxmj.jpg
总的来说,卡通的效果是“颜色更加鲜艳”“细节比较少”等,是一个比较主观的定义;对于”你的名字“这个效果来说,应该还有一个色调的变换
那么,就以表面模糊、彩色直方图均衡以及色相调整这些比较容易实现的方法来达到目的。
 step1.双边滤波
img_f26e7f6b2c694a414b811ec28e88514c.jpe
step2.彩色直方图
img_a3405e58c79817df40cf3a0fac70de10.jpe
step3.饱和度调整
img_82b167afbe611d763bbb5c386e1ad593.jpe


    /************************************************************************/
    /* 3.卡通画处理                                                            */
    /************************************************************************/
    //双边滤波
    bilateralFilter(mixed_clone,temp,5,10.0,2.0);
    //彩色直方图均衡,将RGB图像转到YCbCr分量,然后对Y分量上的图像进行直方图均衡化
    cvtColor(temp,temp,COLOR_BGR2YCrCb);
    split(temp,planes);
    equalizeHist(planes[0],planes[0]);
    merge(planes,temp);
    cvtColor(temp,temp,COLOR_YCrCb2BGR);
    //提高饱和度
    Mat Img_out(temp.size(), CV_32FC3);  
    temp.convertTo(Img_out, CV_32FC3);  
    Mat Img_in(temp.size(), CV_32FC3);  
    temp.convertTo(Img_in, CV_32FC3);  
    // define the iterator of the input image  
    MatIterator_<Vec3f> inp_begin, inp_end;  
    inp_begin=Img_in.begin<Vec3f>();  
    inp_end =Img_in.end<Vec3f>();  
    // define the iterator of the output image  
    MatIterator_<Vec3f> out_begin, out_end;  
    out_begin=Img_out.begin<Vec3f>();  
    out_end =Img_out.end<Vec3f>();  
    // increment (-100.0, 100.0)  
    float Increment=50.0/100.0;   //饱和度参数调整
    float delta=0;  
    float minVal, maxVal;  
    float t1, t2, t3;  
    float L,S;  
    float alpha;  
    for(; inp_begin!=inp_end; inp_begin++, out_begin++)  
    {  
        t1=(*inp_begin)[0];  
        t2=(*inp_begin)[1];  
        t3=(*inp_begin)[2];  
        minVal=std::min(std::min(t1,t2),t3);  
        maxVal=std::max(std::max(t1,t2),t3);  
        delta=(maxVal-minVal)/255.0;  
        L=0.5*(maxVal+minVal)/255.0;  
        S=std::max(0.5*delta/L, 0.5*delta/(1-L));  
        if (Increment>0)  
        {  
            alpha=max(S, 1-Increment);  
            alpha=1.0/alpha-1;  
            (*out_begin)[0]=(*inp_begin)[0]+((*inp_begin)[0]-L*255.0)*alpha;  
            (*out_begin)[1]=(*inp_begin)[1]+((*inp_begin)[1]-L*255.0)*alpha;  
            (*out_begin)[2]=(*inp_begin)[2]+((*inp_begin)[2]-L*255.0)*alpha;  
        }  
        else  
        {  
            alpha=Increment;  
            (*out_begin)[0]=L*255.0+((*inp_begin)[0]-L*255.0)*(1+alpha);  
            (*out_begin)[1]=L*255.0+((*inp_begin)[1]-L*255.0)*(1+alpha);  
            (*out_begin)[2]=L*255.0+((*inp_begin)[2]-L*255.0)*(1+alpha);  
        }  
    }  
    Img_out /=255;
    Img_out.convertTo(matDst,CV_8UC3,255);
算法部分各种结果展现
img_fbc5270f03024f4280ea102692aeeb2c.jpe
img_80c5f0fda540ce9764c09cb66e9237a8.jpe
img_d6dc86ec9025c98e0f5e1951edf9c7c7.jpe
img_d32a7de9a3a08952fdcb4b941cff5bc7.jpe
img_240499a2315bb94b30ee06fa8d61df1d.jpe
现在看来,结果还是不很想”你的名字“的效果,但是美化的效果是有的。存在比较大的硬伤就是天空算法中,会找到一些边界,比如。 img_7db18b6ec1b5ff4a2d5582d4df28d2d2.jpe
为什么会出现这种情况?因为seamlessClone只是clone算法,而不是融合算法,所以需要后续优化。
四、工程实现
基于GOMfcTemplate2,实现图像的输入输出、滤镜操作的参数选择等基础操作
下一步,还需要继续研究清楚各种算法的原理,得到更好的结果。
img_81ba8ff1728dce2b2ab6dded8ecb2bcd.jpe

   2017年7月25日19:18:45 前面的问题没有解决完,耽搁了几天:
一个是最上面边缘的不和谐过渡问题,我重新学习了一下《实现<你的名字>同款滤镜》,他的解决方法更好,主要是在seamless的时候,不是全图融合,而是找出轮廓后再融合。
//2017年7月25日 添加寻找白色区域最大外接矩形的代码
    VP maxCountour = FindBigestContour(mask);
    Rect maxRect = boundingRect(maxCountour);
    if (maxRect.height == 0 || maxRect.width == 0)
       maxRect =  Rect(0,0,mask.cols,mask.rows);//特殊情况
    
    //暴力拷贝
    matDst = matSrc.clone();
    resize(matCloud,matCloud,matDst.size());
    matCloud.copyTo(matDst,mask);
    //为seamless准备材料
    resize(matCloud,matCloud,maxRect.size());
    //seamless clone
    Point center =  Point ((maxRect.x+maxRect.width)/2,(maxRect.y+maxRect.height)/2);//中间位置为蓝天的背景位置
    Mat normal_clone;
    Mat mixed_clone;
    Mat monochrome_clone;
    seamlessClone(matCloud, matSrc, mask, center, normal_clone, NORMAL_CLONE);
    seamlessClone(matCloud, matSrc, mask, center, mixed_clone, MIXED_CLONE);
    seamlessClone(matCloud, matSrc, mask, center, monochrome_clone, MONOCHROME_TRANSFER);
这个时候,结果就更是perfect了,和原文一样,我采用 NORMAL_CLONE  的方法,目前的结果为
img_ce94d01b0f85636211c11e1643d1e4dc.jpe
而在曾经出现过问题的地方,有所收敛
img_2a02b8a5f55ec99816c55302e342708c.jpe
好看多了
img_976b99b317403ecaadd22156c7e35d46.jpe
修改代码的时候需要注意mask的修改,这点在代码里面说明了,自己看。
二个是,我感觉现在前景虽然对比度提高,颜色鲜艳,但明显不是动漫的感觉呀。漫画的效果肯定是有一些论文的,
依稀记得《master opencv with practical projects》第一章实现cartoon效果,
img_537a5de3efb1bf250c35b7db6d649776.jpe
但是这个效果也太随意了(这篇文章在作者一定是个爸爸,妈妈是不会放自己小孩这样的图片的)。找到它的代码进行修改
img_b987d9c9c7b19731b5a8934fbde1c031.jpe
应该还可以进一步提高,但是这个效果很有意思,开源之,大家可以一起来看。
2017年7月28日19:21:27
进一步思考,这种融合效果能否用于图像拼接?进行了一些常识
     Mat matreult  =  Mat(normal_clone.rows  +   30 ,matCloud.cols,CV_8UC3,Scalar( 0 ));
    Mat halfTop = matreult(Rect(0,0,maxRect.width,30));
    Mat halfDown= matreult(Rect(0,30,normal_clone.cols,normal_clone.rows));
    matTop.copyTo(halfTop);
    normal_clone.copyTo(halfDown);
    Mat matNormal = matreult.clone();
    mixed_clone.copyTo(halfDown);
    Mat matMixed = matreult.clone();
    monochrome_clone.copyTo(halfDown);
    Mat matMonochrome = matreult.clone();
img_44ca93a68474c898300a2fb866aac1c9.jpe
可以看到,在边缘部分,由于meanlessclone进行了全局的处理,所以和原图的差距比较大,反而造成了新的误差,所以可能直接适用于图像拼接不适合。




目前方向:图像拼接融合、图像识别 联系方式:jsxyhelu@foxmail.com
目录
相关文章
|
7月前
二次元风格地址发布页源码
二次元风格地址发布页源码
154 1
二次元风格地址发布页源码
|
6月前
|
人工智能 缓存 Java
技术经验解读:【转】详细解析用C#写的小游戏《彩色连珠》(附源代码)
技术经验解读:【转】详细解析用C#写的小游戏《彩色连珠》(附源代码)
26 0
|
IDE 编译器 开发工具
|
缓存 iOS开发 MacOS
万能动态滤镜GIF框架想不想要的?
Wintersweet是一款快速让控件播放GIF和添加过滤器的框架。
万能动态滤镜GIF框架想不想要的?
|
JSON 人工智能 算法
第二节课——【分割抠图功能讲解及演示】|学习笔记
快速学习第二节课——【分割抠图功能讲解及演示】
面向小白!在Unity中实现最简单的人物移动脚本
网上关于角色移动的文章太多太多了,就我自己整理的时候都发现写了好多篇(因为有不同的方案),今天就将目前已知的移动角色的方案总结出来,毕竟是一个资源整合的时代,谁也不想找个角色移动的脚本都要找好几篇文章对吧 目前可以划分为三个方面 • 角色移动到鼠标点击的位置 • 键盘控制角色移动(其他的比如游戏手柄也算键盘、HTC手柄 也算键盘) • 手机端转盘控制角色移动 其他的比如摄像机跟随移动这个可以作为拓展
|
前端开发
我用css精灵图拼接了自己的英文名字,不会还有人不知道精灵图技术吧?
今天学习css精灵图技术,并且通过用它拼接自己的英文名字,拿起小本本记好了哦
187 0
我用css精灵图拼接了自己的英文名字,不会还有人不知道精灵图技术吧?
|
机器学习/深度学习 计算机视觉 异构计算
使用opencv实现实例分割,一学就会|附源码
本文讲述如何使用opencv的dnn模块去与其它成熟深度学习工具相连接,并实现语义分割任务。
5576 0
|
JavaScript Android开发
第二十二章:动画(十四)
你自己的等待动画在本章的下一节中,您将看到Xamarin.Forms实现的基础动画基础结构。这些底层方法允许您定义自己的动画函数,这些函数返回Task对象,并且可以与await一起使用。在第20章“异步和文件I / O”中,您了解了如何使用静态Task.Run方法创建执行的辅助线程,以执行像Mandelbrot计算这样的密集后台作业。
737 0
|
JavaScript Android开发 iOS开发
第二十二章:动画(十二)
永远的动画在入口动画的相反极端是永远的动画。 应用程序可以实现“永远”或至少在程序结束之前进行的动画。 这种动画的唯一目的通常是展示动画系统的功能,但最好是以令人愉快或有趣的方式。第一个示例称为FadingTextAnimation,并使用FadeTo淡入和淡出两个Label元素。
664 0