Android图片素描效果

来源:互联网 发布:公共卫生论文方向知乎 编辑:程序博客网 时间:2024/04/28 02:44

现在很多相机应用都有图片滤镜,实现各种效果,素描是其中一种,我们怎么实现呢?

找到个栗子->使用canvas把照片转换成素描画,作者把原理讲的很清楚了,包括以下几步:

  1. 去色,将图片变为灰度图,即黑白图;
  2. 反相,得到每个像素的补色,具体效果就像照片的底片;
  3. 高斯模糊,把反相后的像素值平均一下;
  4. 颜色减淡,将第1步中的像素和第3步得到的像素值进行计算。
作者已经将代码托管到了github,但是是js写的,这里就是把它转换成android用的java而已…

去色

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;}

反相

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[i];int r = 255 - (color & 0x00ff0000) >> 16;int g = 255 - (color & 0x0000ff00) >> 8;int b = 255 - (color & 0x000000ff);result[i] = r << 16 | g << 8 | b | 0xff000000;}return result;}

高斯模糊

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 Matrixfloat[] 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[i] = g;gaussSum += g;}for (int i = 0, length = gaussMatrix.length; i < length; ++i) {gaussMatrix[i] /= gaussSum;}// x directionfor (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 directionfor (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;}}}

颜色减淡

public static void colorDodge(int[] baseColor, int[] mixColor) {for (int i = 0, length = baseColor.length; i < length; ++i) {int bColor = baseColor[i];int br = (bColor & 0x00ff0000) >> 16;int bg = (bColor & 0x0000ff00) >> 8;int bb = (bColor & 0x000000ff);int mColor = mixColor[i];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[i] = 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;}
最后将这些过程组合起来就可以得到一个素描画
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;}
下面是测试的实际效果图


从左到右依次为原图、灰度图、灰度反相图和最终的素描图。

其实由于灰度图对应到rgb空间后r、g、b的值是相同的,所以去色后面的计算过程可以简化,比如反相

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[i];int b = 255 - (color & 0x000000ff);result[i] = b << 16 | b << 8 | b | 0xff000000;}return result;}
这样可以加快一些计算速度,不过对于手机来说计算量还是太大,需要时间,高斯运算的时候需要指定采样半径,半径越大,模糊程度越高,计算次数随之也会增加,上面的取样半径都是10个像素,下面一张图是直接将原图高斯模糊的效果


代码看这里->http://download.csdn.net/detail/xu_fu/7068275

0 0