现在很多相机应用都有图片滤镜,实现各种效果,素描是其中一种,我们怎么实现呢? 找到个栗子->使用canvas把照片转换成素描画,作者把原理讲的很清楚了,包括以下几步:
- 去色,将图片变为灰度图,即黑白图;
- 反相,得到每个像素的补色,具体效果就像照片的底片;
- 高斯模糊,把反相后的像素值平均一下;
- 颜色减淡,将第1步中的像素和第3步得到的像素值进行计算。
作者已经将代码托管到了github,但是是js写的,这里就是把它转换成android用的java而已…
去色[mw_shl_code=java,true] public static int[] discolor(Bitmap bitmap) {
int picHeight = bitmap.getHeight();
int picWidth = bitmap.getWidth();
int[] pixels = new int[picWidth * picHeight];
bitmap.getPixels(pixels, 0, picWidth, 0, 0, picWidth, picHeight);
for (int i = 0; i < picHeight; ++i) {
for (int j = 0; j < picWidth; ++j) {
int index = i * picWidth + j;
int color = pixels[index];
int r = (color & 0x00ff0000) >> 16;
int g = (color & 0x0000ff00) >> 8;
int b = (color & 0x000000ff);
int grey = (int) (r * KR + g * KG + b * KB);
pixels[index] = grey << 16 | grey << 8 | grey | 0xff000000;
}
}
return pixels;
}[/mw_shl_code]反相[mw_shl_code=java,true]public static int[] reverseColor(int[] pixels) {
int length = pixels.length;
int[] result = new int[length];
for (int i = 0; i < length; ++i) {
int color = pixels;
int r = 255 - (color & 0x00ff0000) >> 16;
int g = 255 - (color & 0x0000ff00) >> 8;
int b = 255 - (color & 0x000000ff);
result = r << 16 | g << 8 | b | 0xff000000;
}
return result;
}[/mw_shl_code]高斯模糊[mw_shl_code=java,true] public static void gaussBlur(int[] data, int width, int height, int radius,
float sigma) {
float pa = (float) (1 / (Math.sqrt(2 * Math.PI) * sigma));
float pb = -1.0f / (2 * sigma * sigma);
// generate the Gauss Matrix
float[] gaussMatrix = new float[radius * 2 + 1];
float gaussSum = 0f;
for (int i = 0, x = -radius; x <= radius; ++x, ++i) {
float g = (float) (pa * Math.exp(pb * x * x));
gaussMatrix = g;
gaussSum += g;
}
for (int i = 0, length = gaussMatrix.length; i < length; ++i) {
gaussMatrix /= gaussSum;
}
// x direction
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
float r = 0, g = 0, b = 0;
gaussSum = 0;
for (int j = -radius; j <= radius; ++j) {
int k = x + j;
if (k >= 0 && k < width) {
int index = y * width + k;
int color = data[index];
int cr = (color & 0x00ff0000) >> 16;
int cg = (color & 0x0000ff00) >> 8;
int cb = (color & 0x000000ff);
r += cr * gaussMatrix[j + radius];
g += cg * gaussMatrix[j + radius];
b += cb * gaussMatrix[j + radius];
gaussSum += gaussMatrix[j + radius];
}
}
int index = y * width + x;
int cr = (int) (r / gaussSum);
int cg = (int) (g / gaussSum);
int cb = (int) (b / gaussSum);
data[index] = cr << 16 | cg << 8 | cb | 0xff000000;
}
}
// y direction
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
float r = 0, g = 0, b = 0;
gaussSum = 0;
for (int j = -radius; j <= radius; ++j) {
int k = y + j;
if (k >= 0 && k < height) {
int index = k * width + x;
int color = data[index];
int cr = (color & 0x00ff0000) >> 16;
int cg = (color & 0x0000ff00) >> 8;
int cb = (color & 0x000000ff);
r += cr * gaussMatrix[j + radius];
g += cg * gaussMatrix[j + radius];
b += cb * gaussMatrix[j + radius];
gaussSum += gaussMatrix[j + radius];
}
}
int index = y * width + x;
int cr = (int) (r / gaussSum);
int cg = (int) (g / gaussSum);
int cb = (int) (b / gaussSum);
data[index] = cr << 16 | cg << 8 | cb | 0xff000000;
}
}
}[/mw_shl_code]颜色减淡[mw_shl_code=java,true] public static void colorDodge(int[] baseColor, int[] mixColor) {
for (int i = 0, length = baseColor.length; i < length; ++i) {
int bColor = baseColor;
int br = (bColor & 0x00ff0000) >> 16;
int bg = (bColor & 0x0000ff00) >> 8;
int bb = (bColor & 0x000000ff);
int mColor = mixColor;
int mr = (mColor & 0x00ff0000) >> 16;
int mg = (mColor & 0x0000ff00) >> 8;
int mb = (mColor & 0x000000ff);
int nr = colorDodgeFormular(br, mr);
int ng = colorDodgeFormular(bg, mg);
int nb = colorDodgeFormular(bb, mb);
baseColor = nr << 16 | ng << 8 | nb | 0xff000000;
}
}
private static int colorDodgeFormular(int base, int mix) {
int result = base + (base * mix) / (255 - mix);
result = result > 255 ? 255 : result;
return result;
}[/mw_shl_code]最后将这些过程组合起来就可以得到一个素描画[mw_shl_code=java,true] public static Bitmap testGaussBlur(Bitmap src, int r, int fai) {
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = Sketch.discolor(src);
int[] copixels = Sketch.simpleReverseColor(pixels);
Sketch.simpleGaussBlur(copixels, width, height, r, fai);
Sketch.simpleColorDodge(pixels, copixels);
Bitmap bitmap = Bitmap.createBitmap(pixels, width, height,
Config.RGB_565);
return bitmap;
}[/mw_shl_code]下面是测试的实际效果图
从左到右依次为原图、灰度图、灰度反相图和最终的素描图。 其实由于灰度图对应到rgb空间后r、g、b的值是相同的,所以去色后面的计算过程可以简化,比如反相[mw_shl_code=java,true]public static int[] simpleReverseColor(int[] pixels) {
int length = pixels.length;
int[] result = new int[length];
for (int i = 0; i < length; ++i) {
int color = pixels;
int b = 255 - (color & 0x000000ff);
result = b << 16 | b << 8 | b | 0xff000000;
}
return result;
}[/mw_shl_code]这样可以加快一些计算速度,不过对于手机来说计算量还是太大,需要时间,高斯运算的时候需要指定采样半径,半径越大,模糊程度越高,计算次数随之也会增加,上面的取样半径都是10个像素,下面一张图是直接将原图高斯模糊的效果
代码看这里->http://download.csdn.net/detail/xu_fu/7068275
|