快速邊緣保留濾波
快速邊緣保留濾波是通過積分圖像實(shí)現(xiàn)局部均方差的邊緣保留模糊算法,計(jì)算簡單而且可以做到計(jì)算量跟半徑無關(guān)亚铁。
首先局部均方差濾波中計(jì)算局部均值的公式如下:
當(dāng)邊緣很弱的時(shí)候系數(shù)K趨近于0蝇刀、該點(diǎn)的矯正之后的像素值就接近平均值。而當(dāng)邊緣很強(qiáng)的時(shí)候系數(shù)K趨近于1徘溢、該點(diǎn)的模糊之后的像素值就接近等于輸入像素值吞琐。上述計(jì)算中最中意的是窗口內(nèi)像素的均值與方差,計(jì)算均值可以根據(jù)積分圖像很容易得到然爆,而計(jì)算方差根據(jù)一系列的數(shù)學(xué)推導(dǎo)可以得到如下的結(jié)果
算法實(shí)現(xiàn)的步驟:
1. 快速邊緣保留濾波
核心的算法如下:
@Override
public ImageProcessor filter(ImageProcessor src) {
// initialization parameters
int width = src.getWidth();
int height = src.getHeight();
xr = yr = (int)(Math.max(width, height) * 0.02);
sigma = 10 + sigma * sigma * 5;
// start ep process
byte[] output = new byte[width*height];
IntIntegralImage ii = new IntIntegralImage();
for(int i=0; i<src.getChannels(); i++) {
System.arraycopy(src.toByte(i), 0, output, 0, output.length);
ii.setImage(src.toByte(i));
ii.process(width, height, true);
processSingleChannel(width, height, ii, output);
System.arraycopy(output, 0, src.toByte(i), 0, output.length);
}
// release memory
output = null;
return src;
}
public void processSingleChannel(int width, int height, IntIntegralImage input, byte[] output) {
float sigma2 = sigma*sigma;
int offset = 0;
int wy = (yr * 2 + 1);
int wx = (xr * 2 + 1);
int size = wx * wy;
int r = 0;
for (int row = yr; row < height-yr; row++) {
offset = row * width;
for (int col = xr; col < width-xr; col++) {
int sr = input.getBlockSum(col, row, wy, wx);
float a = input.getBlockSquareSum(col, row, wy, wx);
float b = sr / size;
float c = (a - (sr*sr)/size)/size;
float d = c / (c+sigma2);
r = (int)((1-d)*b + d*r);
output[offset + col] = (byte)Tools.clamp(r);
}
}
}
其中站粟,IntIntegralImage封裝了積分圖像的算法,具體可以查看 cv4j 中的實(shí)現(xiàn)施蜜。
2. 皮膚檢測
基于RGB顏色空間的簡單閾值膚色識別來實(shí)現(xiàn)皮膚檢測卒蘸,算法如下:
R>95 And G>40 And B>20 And R>G And R>B And Max(R,G,B)-Min(R,G,B)>15 And Abs(R-G)>15
public class DefaultSkinDetection implements ISkinDetection{
// RGB Color model pixel skin detection method
// (R, G, B) is classified as skin if:
// R > 95 and G > 40 and B > 20 and
// max(R, G, B) - min(R, G, B) > 15 and
// |R-G| > 15 and R > G and R > B
//===============================================
@Override
public boolean findSkin(int tr, int tg, int tb) {
return isSkin(tr, tg, tb);
}
@Override
public boolean isSkin(int tr, int tg, int tb) {
int max = Math.max(tr, Math.max(tg, tb));
int min = Math.min(tr, Math.min(tg, tb));
int rg = Math.abs(tr - tg);
if(tr > 95 && tg > 40 && tb > 20 && rg > 15 &&
(max - min) > 15 && tr > tg && tr > tb) {
return true;
} else {
return false;
}
}
}
3. 梯度濾波
梯度濾波器也叫高通濾波器。梯度濾波器有好幾種不同方式翻默,在這里用的是Sobel缸沃。
Sobel算子根據(jù)像素點(diǎn)上下、左右鄰點(diǎn)灰度加權(quán)差修械,在邊緣處達(dá)到極值這一現(xiàn)象檢測邊緣趾牧。對噪聲具有平滑作用,提供較為精確的邊緣方向信息肯污,邊緣定位精度不夠高腰根。當(dāng)對精度要求不是很高時(shí)蔑歌,是一種較為常用的邊緣檢測方法。由于Sobel算法簡單效率高,所以我們在這里選擇它溯警。
4. BeautySkinFilter
結(jié)合以上三步,在 cv4j 中實(shí)現(xiàn)人臉磨皮的濾鏡BeautySkinFilter
package com.cv4j.core.filters;
import com.cv4j.core.datamodel.ByteProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
/**
* Created by gloomy fish on 2017/4/23.
*/
public class BeautySkinFilter implements CommonFilter {
@Override
public ImageProcessor filter(ImageProcessor src) {
int width = src.getWidth();
int height = src.getHeight();
byte[] R = new byte[width*height];
byte[] G = new byte[width*height];
byte[] B = new byte[width*height];
System.arraycopy(src.toByte(0), 0, R, 0, R.length);
System.arraycopy(src.toByte(1), 0, G, 0, G.length);
System.arraycopy(src.toByte(2), 0, B, 0, B.length);
FastEPFilter epFilter = new FastEPFilter();
epFilter.filter(src);
ISkinDetection skinDetector = new DefaultSkinDetection();
int r = 0, g = 0, b = 0;
for(int i=0; i<R.length; i++) {
r = R[i]&0xff;
g = G[i]&0xff;
b = B[i]&0xff;
if(!skinDetector.isSkin(r, g, b)) {
src.toByte(0)[i] = (byte)r;
src.toByte(1)[i] = (byte)g;
src.toByte(2)[i] = (byte)b;
}
}
byte[] gray = new byte[width*height];
int c = 0;
for(int i=0; i<R.length; i++) {
r = R[i] & 0xff;
g = G[i] & 0xff;
b = B[i] & 0xff;
c = (int)(0.299 *r + 0.587*g + 0.114*b);
gray[i] = (byte)c;
}
GradientFilter gradientFilter = new GradientFilter();
int[] gradient = gradientFilter.gradient(new ByteProcessor(gray, width, height));
gray = null;
for(int i=0; i<R.length; i++) {
r = R[i]&0xff;
g = G[i]&0xff;
b = B[i]&0xff;
if(gradient[i] > 50) {
src.toByte(0)[i] = (byte)r;
src.toByte(1)[i] = (byte)g;
src.toByte(2)[i] = (byte)b;
}
}
return src;
}
}
5. 最終效果
BeautySkinFilter跟原先的濾鏡用法是一樣的揭北,一行代碼就可以實(shí)現(xiàn)想要的效果:)
RxImageData.bitmap(bitmap).addFilter(new BeautySkinFilter()).into(image1);
來看看在 Android 上的最終效果:
總結(jié):
cv4j 是gloomyfish和我一起開發(fā)的圖像處理庫寨辩,純java實(shí)現(xiàn),目前還處于早期的版本锄奢。這次的人臉磨皮算法也還有改進(jìn)空間失晴,未來我們還會(huì)繼續(xù)優(yōu)化該算法。
說來很慚愧拘央,由于我們的工作都比較繁忙涂屁,沒有來得及完善開發(fā)文檔。在馬上到來的五一期間灰伟,我們會(huì)補(bǔ)上文檔拆又,未來也會(huì)做出更加酷炫的功能。
先前的文章:
二值圖像分析:案例實(shí)戰(zhàn)(文本分離+硬幣計(jì)數(shù))
Java實(shí)現(xiàn)高斯模糊和圖像的空間卷積
Java實(shí)現(xiàn)圖片濾鏡的高級玩法
Java實(shí)現(xiàn)圖片的濾鏡效果