Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 皮膚亮白白——美顏算法

皮膚亮白白——美顏算法

編輯:關於Android編程

找到第一份實習,老大給我的第一個任務是實現美顏功能,網上找了一大堆資料,總的來說美顏的實現的步驟是:

1.用具有保邊效果的濾波算法對圖像進行模糊處理

2.用膚色檢測算法保護非皮膚區域

3.將模糊後的圖像和原圖進行圖像融合

4.對融合後的圖像進行銳化處理

 

對於步驟一,該濾波算法可以選擇雙邊濾波,導向濾波,表面模糊等,只要能保邊緣就行,高斯模糊是不行的,色斑逗逗就是在這一步磨掉的哈哈,這一步運算速度將直接影響到最後美顏的效率,這也是可以各顯神通的地方。

 

對於步驟二,第一次聽說膚色檢測好像很高大上,但是它的算法非常簡單,就是根據像素的rgb值去判斷而已

 

對於步驟三,可以采用基於alpha通道的圖像融合,這一步的作用是為了增加皮膚的質感,因為第一步一般都把皮膚磨得跟娃娃一樣了感覺很假。

 

對於步驟四,在步驟三處理後,會發現圖像還是有點朦胧感,還是第一步的副作用,銳化可以強化邊緣,讓圖像看起來更清晰,關於銳化的算法網上有不同的實現算法

 

下面就貼出我自己做的美顏源代碼:

 

package com.zhangsutao.utils;

import android.graphics.Bitmap;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * Created by zhangsutao on 2016/3/30.
 */
public class BeautifyMultiThread {

	/**
	*核心函數,請不要再主線程調用
	*params:bit原圖,sigma美顏程度建議范圍(1-20)
	*return 美顏後的圖片
	*/
    public Bitmap beautifyImg(Bitmap bit, int sigma){
        final int width=bit.getWidth();
        final int height=bit.getHeight();
        //原圖
        int[] src_pixels=new int[width*height];

        //結果圖
        int[] res_pixels=new int[width*height];

        bit.getPixels(src_pixels, 0, width, 0, 0, width, height);
        int div=height/5;
        int radius=(int)(Math.max(width,height)*0.02);
        CyclicBarrier barrier=new CyclicBarrier(5);
        Thread t1=new Thread(new FilterTask(barrier,src_pixels,res_pixels,width,height,radius,sigma,50,0,div));
        Thread t2=new Thread(new FilterTask(barrier,src_pixels,res_pixels,width,height,radius,sigma,50,div+1,2*div));
        Thread t3=new Thread(new FilterTask(barrier,src_pixels,res_pixels,width,height,radius,sigma,50,2*div+1,3*div));
        Thread t4=new Thread(new FilterTask(barrier,src_pixels,res_pixels,width,height,radius,sigma,50,3*div+1,4*div));
        Thread t5=new Thread(new FilterTask(barrier,src_pixels,res_pixels,width,height,radius,sigma,50,4*div+1,height-1));

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        try {
	    t1.join();
            t2.join();
            t3.join();
            t4.join();
            t5.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Bitmap resImg=Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        resImg.setPixels(src_pixels, 0, width, 0, 0, width, height);
        return resImg;
    }

    public  boolean isSkin(int r,int g,int b){
        if(r>95&&g>40&&b>20&&r>g&&r>b&&(max(r,g,b)-min(r,g,b)>15)&&Math.abs(r-g)>15){
            return true;
        }else{
            return false;
        }
    }
    public  int min(int a,int b,int c){
        if(a>b)
            a=b;
        if(a>c)
            a=c;
        return a;
    }
    public  int max(int a,int b,int c){
        if(a> 8) & 0xff;
                int r = (tmpPixels >> 16) & 0xff;
                r_col_sum += r;
                g_col_sum += g;
                b_col_sum += b;
                r_squcol_sum+=r*r;
                g_squcol_sum+=g*g;
                b_squcol_sum+=b*b;
            }
            rwindow[y]=r_col_sum;
            gwindow[y]=g_col_sum;
            bwindow[y]=b_col_sum;

            r_squ_window[y]=r_squcol_sum;
            g_squ_window[y]=g_squcol_sum;
            b_squ_window[y]=b_squcol_sum;
        }

        //開始遍歷圖片
        for(int i=startRaw;i<=endRaw;i++) {
            //計算第一個sum值
            rsum=0;bsum=0;gsum=0;
            r_squ_sum=0;b_squ_sum=0;g_squ_sum=0;
            oldP=array[i*width];
            old_b = oldP & 0xff;
            old_g = (oldP >> 8) & 0xff;
            old_r = (oldP >> 16) & 0xff;
            for(int x=-radius;x<=radius;x++) {
                int inkx=edgeHandle(x,width);
                //算出和
                rsum+=rwindow[inkx];
                gsum+=gwindow[inkx];
                bsum+=bwindow[inkx];
                //平方和
                r_squ_sum+=r_squ_window[inkx];
                g_squ_sum+=g_squ_window[inkx];
                b_squ_sum+=b_squ_window[inkx];
            }
            //根據方差和均值算出新像素
            mean_r=rsum/filter_win;
            mean_g=gsum/filter_win;
            mean_b=bsum/filter_win;
            var_r=((float)r_squ_sum-(float)rsum*rsum/(float)filter_win)/(float)filter_win;
            var_g=((float)g_squ_sum-(float)gsum*gsum/(float)filter_win)/(float)filter_win;
            var_b=((float)b_squ_sum-(float)bsum*bsum/(float)filter_win)/(float)filter_win;
            tmp=var_r/(var_r+sigma);
            new_r= (int) ((1-tmp)*mean_r+tmp*old_r);
            tmp=var_g/(var_g+sigma);
            new_g= (int) ((1-tmp)*mean_g+tmp*old_g);
            tmp=var_b/(var_b+sigma);
            new_b= (int) ((1-tmp)*mean_b+tmp*old_b);

            //融合+膚色檢測
            if(isSkin(new_r,new_g,new_b)){
                new_b=(old_b*alpha+new_b*(255-alpha))>>8;
                new_g=(old_g*alpha+new_g*(255-alpha))>>8;
                new_r=(old_r*alpha+new_r*(255-alpha))>>8;
            }else{
                new_b=old_b;
                new_g=old_g;
                new_r=old_r;
            }

            res[i*width]=((new_r & 0xff) << 16) | ((new_g & 0xff) << 8) | (new_b & 0xff);


            for (int j = 1; j < width; j++) {
                oldP=array[i*width+j];
                old_b = oldP & 0xff;
                old_g = (oldP >> 8) & 0xff;
                old_r = (oldP >> 16) & 0xff;

                int addx=edgeHandle(j+radius,width);
                int subx=edgeHandle(j-radius-1,width);

                rsum=rsum+rwindow[addx]-rwindow[subx];
                gsum=gsum+gwindow[addx]-gwindow[subx];
                bsum=bsum+bwindow[addx]-bwindow[subx];

                r_squ_sum=r_squ_sum+r_squ_window[addx]-r_squ_window[subx];
                g_squ_sum=g_squ_sum+g_squ_window[addx]-g_squ_window[subx];
                b_squ_sum=b_squ_sum+b_squ_window[addx]-b_squ_window[subx];

                mean_r=rsum/filter_win;
                mean_g=gsum/filter_win;
                mean_b=bsum/filter_win;
                var_r=((float)r_squ_sum-(float)rsum*rsum/(float)filter_win)/(float)filter_win;
                var_g=((float)g_squ_sum-(float)gsum*gsum/(float)filter_win)/(float)filter_win;
                var_b=((float)b_squ_sum-(float)bsum*bsum/(float)filter_win)/(float)filter_win;

                tmp=var_r/(var_r+sigma);
                new_r= (int) ((1-tmp)*mean_r+tmp*old_r);
                tmp=var_g/(var_g+sigma);
                new_g= (int) ((1-tmp)*mean_g+tmp*old_g);
                tmp=var_b/(var_b+sigma);
                new_b= (int) ((1-tmp)*mean_b+tmp*old_b);

                //融合+膚色檢測
                if(isSkin(new_r,new_g,new_b)){
                    new_b=(old_b*alpha+new_b*(255-alpha))>>8;
                    new_g=(old_g*alpha+new_g*(255-alpha))>>8;
                    new_r=(old_r*alpha+new_r*(255-alpha))>>8;
                }else{
                    new_b=old_b;
                    new_g=old_g;
                    new_r=old_r;
                }

                res[i*width+j]=((new_r & 0xff) << 16) | ((new_g & 0xff) << 8) | (new_b & 0xff);
            }
            //更新window數組
            for(int y=0;y> 8) & 0xff;
                old_r = (tmpPixels >> 16) & 0xff;
                tmpPixels = array[addp*width+y];
                new_b = tmpPixels & 0xff;
                new_g = (tmpPixels >> 8) & 0xff;
                new_r = (tmpPixels >> 16) & 0xff;
                rwindow[y]=rwindow[y]+new_r-old_r;
                gwindow[y]=gwindow[y]+new_g-old_g;
                bwindow[y]=bwindow[y]+new_b-old_b;

                r_squ_window[y]=r_squ_window[y]+new_r*new_r-old_r*old_r;
                g_squ_window[y]=g_squ_window[y]+new_g*new_g-old_g*old_g;
                b_squ_window[y]=b_squ_window[y]+new_b*new_b-old_b*old_b;
            }
        }
        return res;
    }
    //邊緣處理
    public int edgeHandle(int index, int w)
    {
        if(index<0)
            return 0;
        else
        if(index>=w)
            return w-1;
        else
            return index;
    }



    //均值濾波的銳化算法
    public int[] sharpen(int[] src,int[] res,int width,int height,int radius,int k,int startRaw,int endRaw){

        //和數組
        int[] rwindow=new int[width];
        int[] gwindow=new int[width];
        int[] bwindow=new int[width];
        //窗口面積
        int filter_win=(radius*2+1);
        filter_win=filter_win*filter_win;
        //窗口內的rgb值得和
        int rsum=0,bsum=0,gsum=0;
        //新的rgb值
        int new_r=0,new_g=0,new_b=0;
        //舊的rgb值
        int old_r=0,old_g=0,old_b=0,oldP=0;
        //窗口平均值
        int mean_r=0,mean_g=0,mean_b=0;
        //窗口增加值,和刪除值
        int addp=0,subp=0;


        //初始化window數組
        for(int y=0;y> 8) & 0xff;
                int r = (tmpPixels >> 16) & 0xff;
                r_col_sum += r;
                g_col_sum += g;
                b_col_sum += b;

            }
            rwindow[y]=r_col_sum;
            gwindow[y]=g_col_sum;
            bwindow[y]=b_col_sum;
        }

        //開始遍歷圖片
        for(int i=startRaw;i<=endRaw;i++) {
            //計算第一個sum值
            rsum=0;bsum=0;gsum=0;

            oldP=src[i*width];
            old_b = oldP & 0xff;
            old_g = (oldP >> 8) & 0xff;
            old_r = (oldP >> 16) & 0xff;
            for(int x=-radius;x<=radius;x++) {
                int inkx=edgeHandle(x,width);
                //算出和
                rsum+=rwindow[inkx];
                gsum+=gwindow[inkx];
                bsum+=bwindow[inkx];

            }
            //根據方差和均值算出新像素
            mean_r=rsum/filter_win;
            mean_g=gsum/filter_win;
            mean_b=bsum/filter_win;


            new_r= range(mean_r+k*(old_r-mean_r));
            new_g= range(mean_g+k*(old_g-mean_g));
            new_b= range(mean_b+k*(old_b-mean_b));

            res[i*width]=((new_r & 0xff) << 16) | ((new_g & 0xff) << 8) | (new_b & 0xff);


            for (int j = 1; j < width; j++) {
                oldP=src[i*width+j];
                old_b = oldP & 0xff;
                old_g = (oldP >> 8) & 0xff;
                old_r = (oldP >> 16) & 0xff;

                int addx=edgeHandle(j+radius,width);
                int subx=edgeHandle(j-radius-1,width);

                rsum=rsum+rwindow[addx]-rwindow[subx];
                gsum=gsum+gwindow[addx]-gwindow[subx];
                bsum=bsum+bwindow[addx]-bwindow[subx];


                mean_r=rsum/filter_win;
                mean_g=gsum/filter_win;
                mean_b=bsum/filter_win;

                new_r= range(mean_r+k*(old_r-mean_r));
                new_g= range(mean_g+k*(old_g-mean_g));
                new_b= range(mean_b+k*(old_b-mean_b));
                res[i*width+j]=((new_r & 0xff) << 16) | ((new_g & 0xff) << 8) | (new_b & 0xff);
            }
            //更新window數組
            for(int y=0;y> 8) & 0xff;
                old_r = (tmpPixels >> 16) & 0xff;
                tmpPixels = src[addp*width+y];
                new_b = tmpPixels & 0xff;
                new_g = (tmpPixels >> 8) & 0xff;
                new_r = (tmpPixels >> 16) & 0xff;
                rwindow[y]=rwindow[y]+new_r-old_r;
                gwindow[y]=gwindow[y]+new_g-old_g;
                bwindow[y]=bwindow[y]+new_b-old_b;
            }
        }
        return res;
    }
    public int range(int i){
        if(i<0)
            return 0;
        else
        if(i>255)
            return 255;
        else
            return i;
    }

}
下面是原圖

\
 

下面是效果圖:

\

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved