模擬油畫和鉛筆畫的濾鏡效果

油畫效果

先上未經任何處理的原圖
java

原圖.png

而後使用油畫風格的濾鏡OilPaintFilter看看效果,OilPaintFilter的使用方式就一句話:)git

RxImageData.bitmap(bitmap).addFilter(new OilPaintFilter()).into(image);複製代碼

油畫效果.png

OilPaintFilter在處理人物圖片和風景圖片時具備比較好的效果。github

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算法,修改局部的像素權重從而實現圖像的像素模糊,以達到近似油畫的效果。ide

鉛筆畫效果

咱們還開發了另外一款濾鏡StrokeAreaFilter,用於模擬鉛筆畫的效果。post

RxImageData.bitmap(bitmap).addFilter(new StrokeAreaFilter()).into(image);複製代碼

看下效果
this

鉛筆畫效果.png

對於鉛筆畫而言可能有點牽強,那再組合一個隨機噪聲的濾鏡試試。spa

RxImageData.bitmap(bitmap)
        .addFilter(new StrokeAreaFilter())
        .addFilter(new GaussianNoiseFilter())
        .into(image);複製代碼

鉛筆畫效果2.png

效果也不是特別好,那再換一個USMFilter試試。.net

RxImageData.bitmap(bitmap)
       .addFilter(new StrokeAreaFilter())
       .addFilter(new USMFilter())
       .into(image);複製代碼

終於,此次效果比前面兩幅效果更好了。3d

鉛筆畫效果3.png

可是,因爲是兩個濾鏡的疊加,速度會慢不少。再者,USMFilter它是繼承高斯濾鏡的。因此,在實際使用中只需單獨使用StrokeAreaFilter便可,細節多少能夠根據參數來調節。

總結

本文所使用的兩款濾鏡OilPaintFilter和StrokeAreaFilter都在cv4j中。

cv4jgloomyfish和我一塊兒開發的圖像處理庫,純java實現,目前還處於早期的版本,目前已經更新了濾鏡的文檔。

上週末咱們作了兩款濾鏡,效果還算是蠻酷的,可是速度在移動端還不夠理想,將來會想辦法對算法作一些改進,以便更好地知足移動端的體驗。

該系列先前的文章:
二值圖像分析之輪廓分析
基於邊緣保留濾波實現人臉磨皮的算法
二值圖像分析---案例實戰(文本分離+硬幣計數)
Java實現高斯模糊和圖像的空間卷積
Java實現圖片濾鏡的高級玩法
Java實現圖片的濾鏡效果

相關文章
相關標籤/搜索