我的第一个任务是实现美颜功能,网上找了一大堆资料,总的来说美颜的实现的步骤是:
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;
}
}
下面是效果图: