模拟油画和铅笔画的滤镜效果

简介: 模拟油画和铅笔画的滤镜效果

油画效果



先上未经任何处理的原图


image.png

原图.png


然后使用油画风格的滤镜OilPaintFilter看看效果,OilPaintFilter的使用方式就一句话:)

RxImageData.bitmap(bitmap).addFilter(new OilPaintFilter()).into(image);


image.png

油画效果.png


OilPaintFilter在处理人物图片和风景图片时具有比较好的效果。


OilPaintFilter的源码如下:

import com.cv4j.core.datamodel.ColorProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
/**
 * Created by Tony Shen on 2017/5/7.
 */
public class OilPaintFilter extends BaseFilter {
    private int radius = 15; // default value
    private int intensity = 40; // default value
    public OilPaintFilter() {
        this(15, 40);
    }
    public OilPaintFilter(int radius, int graylevel) {
        this.radius = radius;
        this.intensity = graylevel;
    }
    public int getRadius() {
        return radius;
    }
    public void setRadius(int radius) {
        this.radius = radius;
    }
    public int getIntensity() {
        return intensity;
    }
    public void setIntensity(int intensity) {
        this.intensity = intensity;
    }
    @Override
    public ImageProcessor doFilter(ImageProcessor src) {
        byte[][] output = new byte[3][R.length];
        int index = 0;
        int subradius = this.radius / 2;
        int[] intensityCount = new int[intensity+1];
        int[] ravg = new int[intensity+1];
        int[] gavg = new int[intensity+1];
        int[] bavg = new int[intensity+1];
        for(int i=0; i<=intensity; i++) {
            intensityCount[i] = 0;
            ravg[i] = 0;
            gavg[i] = 0;
            bavg[i] = 0;
        }
        for(int row=0; row<height; row++) {
            int ta = 0, tr = 0, tg = 0, tb = 0;
            for(int col=0; col<width; col++) {
                for(int subRow = -subradius; subRow <= subradius; subRow++)
                {
                    for(int subCol = -subradius; subCol <= subradius; subCol++)
                    {
                        int nrow = row + subRow;
                        int ncol = col + subCol;
                        if(nrow >=height || nrow < 0)
                        {
                            nrow = 0;
                        }
                        if(ncol >= width || ncol < 0)
                        {
                            ncol = 0;
                        }
                        index = nrow * width + ncol;
                        tr = R[index] & 0xff;
                        tg = G[index] & 0xff;
                        tb = B[index] & 0xff;
                        int curIntensity = (int)(((double)((tr+tg+tb)/3)*intensity)/255.0f);
                        intensityCount[curIntensity]++;
                        ravg[curIntensity] += tr;
                        gavg[curIntensity] += tg;
                        bavg[curIntensity] += tb;
                    }
                }
                // find the max number of same gray level pixel
                int maxCount = 0, maxIndex = 0;
                for(int m=0; m<intensityCount.length; m++)
                {
                    if(intensityCount[m] > maxCount)
                    {
                        maxCount = intensityCount[m];
                        maxIndex = m;
                    }
                }
                // get average value of the pixel
                int nr = ravg[maxIndex] / maxCount;
                int ng = gavg[maxIndex] / maxCount;
                int nb = bavg[maxIndex] / maxCount;
                index = row * width + col;
                output[0][index] = (byte) nr;
                output[1][index] = (byte) ng;
                output[2][index] = (byte) nb;
                // post clear values for next pixel
                for(int i=0; i<=intensity; i++)
                {
                    intensityCount[i] = 0;
                    ravg[i] = 0;
                    gavg[i] = 0;
                    bavg[i] = 0;
                }
            }
        }
        ((ColorProcessor) src).putRGB(output[0], output[1], output[2]);
        output = null;
        return src;
    }
}


其原理是使用边缘保留滤波,边缘保留滤波有很多种,可以参考之前的一篇文章基于边缘保留滤波实现人脸磨皮的算法。这里主要用的是Mean shift算法,修改局部的像素权重从而实现图像的像素模糊,以达到近似油画的效果。


铅笔画效果



我们还开发了另一款滤镜StrokeAreaFilter,用于模拟铅笔画的效果。

RxImageData.bitmap(bitmap).addFilter(new StrokeAreaFilter()).into(image);

看下效果


image.png

铅笔画效果.png


对于铅笔画而言可能有点牵强,那再组合一个随机噪声的滤镜试试。

RxImageData.bitmap(bitmap)
        .addFilter(new StrokeAreaFilter())
        .addFilter(new GaussianNoiseFilter())
        .into(image);

image.png

铅笔画效果2.png


效果也不是特别好,那再换一个USMFilter试试。

RxImageData.bitmap(bitmap)
       .addFilter(new StrokeAreaFilter())
       .addFilter(new USMFilter())
       .into(image);

终于,这次效果比前面两幅效果更好了。

image.png

铅笔画效果3.png


但是,由于是两个滤镜的叠加,速度会慢很多。再者,USMFilter它是继承高斯滤镜的。所以,在实际使用中只需单独使用StrokeAreaFilter即可,细节多少可以根据参数来调节。


总结



本文所使用的两款滤镜OilPaintFilter和StrokeAreaFilter都在cv4j中。


cv4jgloomyfish和我一起开发的图像处理库,纯java实现,目前还处于早期的版本,目前已经更新了滤镜的文档。


上周末我们做了两款滤镜,效果还算是蛮酷的,但是速度在移动端还不够理想,未来会想办法对算法做一些改进,以便更好地满足移动端的体验。

相关文章
|
7月前
|
算法 Shell 计算机视觉
【特效】对实时动态人脸进行马赛克及贴图马赛克处理及一些拓展
【特效】对实时动态人脸进行马赛克及贴图马赛克处理及一些拓展
166 0
OpenCV-怀旧色滤镜
OpenCV-怀旧色滤镜
102 1
|
机器学习/深度学习
识别手绘数字图像
识别手绘数字图像
89 0
|
算法 C#
图像滤镜艺术---图像光照效果滤镜
原文:图像滤镜艺术---图像光照效果滤镜     图像光照滤镜     图像光照提滤镜主要是模拟阳光照射下的图像效果,通常使用在暗光环境下拍摄的照片中,或者是没有太阳的日景照片中。
1124 0
|
知识图谱 C# C++
图像滤镜艺术---乐高像素拼图特效滤镜的代码实现
原文:图像滤镜艺术---乐高像素拼图特效滤镜的代码实现 这篇博客承接上一篇http://blog.csdn.net/trent1985/article/details/50195747乐高像素拼图特效的PS实现,主要是一些博友告诉我如何用代码实现这个PS的特效,为此,这里以这个特效为例,来说一下。
1092 0
|
算法 C#
图像滤镜艺术---旋转模糊滤镜
原文:图像滤镜艺术---旋转模糊滤镜本文介绍一种旋转模糊滤镜的实现算法。 旋转模糊主要特点是:整张图像围绕一个中心点做旋转变换,同时有个控制旋转程度的变量和一个控制模糊程度的变量,来完成这个效果。图像中距离中心点越近,旋转和模糊的程度都越小,反之,越大。
1054 0
|
算法 C#
图像滤镜艺术---(Nostalgla Filter)老照片滤镜
原文:图像滤镜艺术---(Nostalgla Filter)老照片滤镜(Nostalgla Filter)老照片滤镜 Nostalgla Filter主要是通过算法来模拟一种复古,陈旧的照片风格,以此来达到新照片显示老照片的效果。
1295 0
|
C#
图像滤镜艺术---怀旧风格滤镜
原文:图像滤镜艺术---怀旧风格滤镜怀旧风格滤镜 本文介绍一款怀旧风格滤镜特效的代码实现,这个滤镜效果跟前面我们介绍的老照片滤镜效果相比,听起来感觉没太大差,实际上老照片不仅 有怀旧的风格,更多了一些怀旧的痕迹,比如照片的褶皱,裂纹等等,而怀旧风格,只是一种发黄的颜色风格而已。
1134 0
|
C# 算法
图像滤镜艺术---挤压(Pinch)滤镜
原文:图像滤镜艺术---挤压(Pinch)滤镜Pinch滤镜 Pinch滤镜是通过坐标变换来实现以某个点(cenX,cenY)为中心,某个半径R内图像向其挤压变形的效果。实现这个滤镜的算法很多,主要是数学公式的不同,大家可以自行设计,这里给个代码示例,大家可以直接使用。
995 0
|
C#
图像滤镜艺术---球面(Spherize)滤镜
原文:图像滤镜艺术---球面(Spherize)滤镜 球面(Spherize)滤镜 球面滤镜是通过极坐标变换实现图像的球面特效。 代码如下:         //         ///         /// Pinch Filter         ///        /// Source image.
1026 0