編輯:關於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; } }
下面是效果圖:
先簡單介紹一下shape的基本屬性:
效果:需求:不論什麼領域,在模仿一個東西的時候,我們首先要對它進行需求提取,這樣才能保證做到”惟妙惟肖”。通過對QQ側滑功能的分析,提取出了以下需
至於fragment的使用就不多說了,直奔主題, 布局文件: //導航欄
在Android Studio裡面想設置代碼風格,在這裡我想把代碼風格從Java的行尾式改成C風格的代碼,如下 if(true) { // TODO}if