編輯:關於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;
}
}

下面是效果圖:

Android xml 深入解析shape
先簡單介紹一下shape的基本屬性:
HorizontalScrollView仿QQ側滑刪除
效果:需求:不論什麼領域,在模仿一個東西的時候,我們首先要對它進行需求提取,這樣才能保證做到”惟妙惟肖”。通過對QQ側滑功能的分析,提取出了以下需
Android解決多個Fragment切換時布局重新實例化問題
至於fragment的使用就不多說了,直奔主題, 布局文件: //導航欄
Android Studio設置代碼風格
在Android Studio裡面想設置代碼風格,在這裡我想把代碼風格從Java的行尾式改成C風格的代碼,如下 if(true) { // TODO}if