Android图片素描效果

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

找到个栗子->使用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 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[i] = g;
			gaussSum += g;
		}

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

颜色减淡

	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;

	}
下面是测试的实际效果图

Android图片素描效果_第1张图片Android图片素描效果_第2张图片Android图片素描效果_第3张图片Android图片素描效果_第4张图片

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

其实由于灰度图对应到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个像素,下面一张图是直接将原图高斯模糊的效果

Android图片素描效果_第5张图片

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

你可能感兴趣的:(Android,Android动效实验室)