Android开发笔记(九十七)图片的特效处理

图片特效用到的函数

本文讲述的图片特效处理包括:怀旧、光照、光晕、底片、浮雕、模糊、锐化、黑白、冰冻、素描,所有这些特效都是基于一定的算法,对图像每个点的RGB值进行计算,并汇总所有点的计算结果生成新图片。


特效处理主要用到Bitmap类的三个方法:
createBitmap : 创建一张新图片。
getPixels : 从指定图片中获取所有点的像素数组。
setPixels : 对指定图片设置所有点的像素数组。


图片怀旧效果

现实生活中的老相片都是泛黄的,而黄色又是由绿色和红色混合而成,所以怀旧效果为了突出黄色,就得加大绿色和红色的比重,同时降低蓝色的比重。


下面是怀旧效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第1张图片



下面是怀旧效果的示例代码:
	// 图片怀旧效果
	public static Bitmap nostalgic(Bitmap bmp) {
		/*
		 * 怀旧处理算法即设置新的RGB R=0.393r+0.769g+0.189b G=0.349r+0.686g+0.168b B=0.272r+0.534g+0.131b
		 */
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int pixColor = 0;
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 0; i < height; i++) {
			for (int k = 0; k < width; k++) {
				pixColor = pixels[width * i + k];
				pixR = Color.red(pixColor);
				pixG = Color.green(pixColor);
				pixB = Color.blue(pixColor);
				newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB);
				newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB);
				newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB);
				int newColor = Color.argb(255, newR > 255 ? 255 : newR,
						newG > 255 ? 255 : newG, newB > 255 ? 255 : newB);
				pixels[width * i + k] = newColor;
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片光照效果

现实生活中的相片,越靠近光源的区域,就显得越明亮。对应到图片中,便是越靠近光源中心的像素,就显得越白,越远离光源中心,就越没那么白。


下面是光照效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第2张图片


下面是光照效果的示例代码:
	// 图片光照效果
	public static Bitmap sunshine(Bitmap bmp) {
		/*
		 * 算法原理:图片上面的像素点按照给定圆心,按照圆半径的变化,像素点的RGB值分别加上相应的值作为当前点的RGB值
		 */
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int pixColor = 0;
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		// 围绕圆形光照
		int centerX = width / 2;
		int centerY = height / 2;
		int radius = Math.min(centerX, centerY);
		float strength = 150F;// 光照强度100-150
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 1; i < height - 1; i++) {
			for (int k = 1; k < width - 1; k++) {
				// 获取前一个像素颜色
				pixColor = pixels[width * i + k];
				pixR = Color.red(pixColor);
				pixG = Color.green(pixColor);
				pixB = Color.blue(pixColor);
				newR = pixR;
				newG = pixG;
				newB = pixB;
				// 计算当前点到光照中心的距离,平面坐标系中两点之间的距离
				int distance = (int) (Math.pow((centerY - i), 2) + Math.pow(
						(centerX - k), 2));
				if (distance < radius * radius) {
					// 按照距离大小计算增强的光照值
					int result = (int) (strength * (1.0 - Math.sqrt(distance) / radius));
					newR = pixR + result;
					newG = newG + result;
					newB = pixB + result;
				}
				newR = Math.min(255, Math.max(0, newR));
				newG = Math.min(255, Math.max(0, newG));
				newB = Math.min(255, Math.max(0, newB));
				pixels[width * i + k] = Color.argb(255, newR, newG, newB);
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片光晕效果

光晕与光照则恰恰相反,光晕表现为光源附近的区域显示正常,而界线以外的区域则显示暗淡。根据显示区域的形状,还可分为圆形光晕和矩形光晕两种常见类型。


下面是光晕效果(圆形光晕)的结果截图:
Android开发笔记(九十七)图片的特效处理_第3张图片


下面是光晕效果(圆形光晕)的示例代码:
	// 图片光晕效果(圆形光晕)
	public static Bitmap haloCircle(Bitmap bmp) {
		// 高斯矩阵
		int[] gauss = new int[] { 1, 2, 1, 2, 4, 2, 1, 2, 1 };

		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int x = width / 2;
		int y = height / 2;
		float r = Math.min(x, y) / 2;

		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int pixColor = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int delta = 24; // 值越小图片会越亮,越大则越暗

		int idx = 0;
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 1, length = height - 1; i < length; i++) {
			for (int j = 1, len = width - 1; j < len; j++) {
				idx = 0;
				int distance = (int) (Math.pow(j - x, 2) + Math.pow(i - y, 2));
				// 不是中心区域的点做模糊处理
				if (distance > r * r) {
					for (int m = -1; m <= 1; m++) {
						for (int n = -1; n <= 1; n++) {
							pixColor = pixels[(i + m) * width + j + n];
							pixR = Color.red(pixColor);
							pixG = Color.green(pixColor);
							pixB = Color.blue(pixColor);

							newR = newR + (int) (pixR * gauss[idx]);
							newG = newG + (int) (pixG * gauss[idx]);
							newB = newB + (int) (pixB * gauss[idx]);
							idx++;
						}
					}

					newR /= delta;
					newG /= delta;
					newB /= delta;
					newR = Math.min(255, Math.max(0, newR));
					newG = Math.min(255, Math.max(0, newG));
					newB = Math.min(255, Math.max(0, newB));

					pixels[i * width + j] = Color.argb(255, newR, newG, newB);
					newR = 0;
					newG = 0;
					newB = 0;
				}
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


下面是光晕效果(矩形光晕)的结果截图:
Android开发笔记(九十七)图片的特效处理_第4张图片


下面是光晕效果(矩形光晕)的示例代码:
	// 图片光晕效果(矩形光晕)
	public static Bitmap haloRect(Bitmap bmp) {
		// 高斯矩阵
		int[] gauss = new int[] { 1, 2, 1, 2, 4, 2, 1, 2, 1 };

		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int top = height / 4;
		int bottom = height / 4 * 3;
		int left = width / 4;
		int right = width / 4 * 3;

		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int pixColor = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int delta = 24; // 值越小图片会越亮,越大则越暗

		int idx = 0;
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 1, length = height - 1; i < length; i++) {
			for (int j = 1, len = width - 1; j < len; j++) {
				idx = 0;
				// 不是中心区域的点做模糊处理
				if (ibottom || jright) {
					for (int m = -1; m <= 1; m++) {
						for (int n = -1; n <= 1; n++) {
							pixColor = pixels[(i + m) * width + j + n];
							pixR = Color.red(pixColor);
							pixG = Color.green(pixColor);
							pixB = Color.blue(pixColor);

							newR = newR + (int) (pixR * gauss[idx]);
							newG = newG + (int) (pixG * gauss[idx]);
							newB = newB + (int) (pixB * gauss[idx]);
							idx++;
						}
					}

					newR /= delta;
					newG /= delta;
					newB /= delta;
					newR = Math.min(255, Math.max(0, newR));
					newG = Math.min(255, Math.max(0, newG));
					newB = Math.min(255, Math.max(0, newB));

					pixels[i * width + j] = Color.argb(255, newR, newG, newB);
					newR = 0;
					newG = 0;
					newB = 0;
				}
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片底片效果

在数码相机时代之前,占统治地位的是胶卷相机,胶卷底片与洗出来的相片相比,底片的RGB值就是相片的RGB值取反,即:底片的红色=255-相片的红色,底片的绿色=255-相片的绿色,底片的蓝色=255-相片的蓝色。


下面是底片效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第5张图片


下面是底片效果的示例代码:
	// 图片底片效果
	public static Bitmap negative(Bitmap bmp) {
		/*
		 * 算法原理: 将当前像素点的RGB值分别与255之差后的值作为当前点的RGB值 例:ABC,求B点的底片效果: B.r = 255 - B.r; B.g = 255 - B.g; B.b = 255 - B.b;
		 */
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int[] oldPixels = new int[width * height];
		int[] newPixels = new int[width * height];
		int color;
		int pixelsR, pixelsG, pixelsB, pixelsA;

		bmp.getPixels(oldPixels, 0, width, 0, 0, width, height);
		for (int i = 1; i < height * width; i++) {
			color = oldPixels[i];
			// 获取RGB分量
			pixelsA = Color.alpha(color);
			pixelsR = Color.red(color);
			pixelsG = Color.green(color);
			pixelsB = Color.blue(color);
			// 转换
			pixelsR = (255 - pixelsR);
			pixelsG = (255 - pixelsG);
			pixelsB = (255 - pixelsB);
			// 均小于等于255大于等于0
			if (pixelsR > 255) {
				pixelsR = 255;
			} else if (pixelsR < 0) {
				pixelsR = 0;
			}
			if (pixelsG > 255) {
				pixelsG = 255;
			} else if (pixelsG < 0) {
				pixelsG = 0;
			}
			if (pixelsB > 255) {
				pixelsB = 255;
			} else if (pixelsB < 0) {
				pixelsB = 0;
			}
			// 根据新的RGB生成新像素
			newPixels[i] = Color.argb(pixelsA, pixelsR, pixelsG, pixelsB);
		}
		bitmap.setPixels(newPixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片浮雕效果

浮雕的算法原理是:用前一个像素点的RGB值分别减去当前像素点的RGB值并加上127作为当前像素点的RGB值。


下面是浮雕效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第6张图片


下面是浮雕效果的示例代码:
	// 图片浮雕效果
	public static Bitmap relief(Bitmap bmp) {
		/*
		 * 算法原理:(前一个像素点RGB-当前像素点RGB+127)作为当前像素点RGB值 在ABC中计算B点浮雕效果(RGB值在0~255)
		 * B.r = C.r - B.r + 127 B.g = C.g - B.g + 127 B.b = C.b - B.b + 127
		 */
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int pixColor = 0;
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 1; i < height - 1; i++) {
			for (int k = 1; k < width - 1; k++) {
				// 获取前一个像素颜色
				pixColor = pixels[width * i + k];
				pixR = Color.red(pixColor);
				pixG = Color.green(pixColor);
				pixB = Color.blue(pixColor);
				// 获取当前像素
				pixColor = pixels[(width * i + k) + 1];
				newR = Color.red(pixColor) - pixR + 127;
				newG = Color.green(pixColor) - pixG + 127;
				newB = Color.blue(pixColor) - pixB + 127;
				newR = Math.min(255, Math.max(0, newR));
				newG = Math.min(255, Math.max(0, newG));
				newB = Math.min(255, Math.max(0, newB));
				pixels[width * i + k] = Color.argb(255, newR, newG, newB);
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片模糊效果

要让一张图片变得模糊起来,基本原理是每个点的颜色都是附近一片像素的颜色综合得到,这样图片中每个景物的边缘就变得模糊了。


下面是模糊效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第7张图片


下面是模糊效果的示例代码:
	/** 水平方向模糊度 */
	private static float hRadius = 10;
	/** 竖直方向模糊度 */
	private static float vRadius = 10;
	/** 模糊迭代度 */
	private static int iterations = 7;
 
	// 图片模糊效果。该算法比较明显
	public static Bitmap blur(Bitmap bmp) {
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		int[] inPixels = new int[width * height];
		int[] outPixels = new int[width * height];
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
		bmp.getPixels(inPixels, 0, width, 0, 0, width, height);

		for (int i = 0; i < iterations; i++) {
			blur(inPixels, outPixels, width, height, hRadius);
			blur(outPixels, inPixels, height, width, vRadius);
		}
		blurFractional(inPixels, outPixels, width, height, hRadius);
		blurFractional(outPixels, inPixels, height, width, vRadius);
		bitmap.setPixels(inPixels, 0, width, 0, 0, width, height);
		return bitmap;
	}

	public static void blur(int[] in, int[] out, int width, int height, float radius) {
		int widthMinus1 = width - 1;
		int r = (int) radius;
		int tableSize = 2 * r + 1;
		int divide[] = new int[256 * tableSize];
		for (int i = 0; i < 256 * tableSize; i++) {
			divide[i] = i / tableSize;
		}
		
		int inIndex = 0;
		for (int y = 0; y < height; y++) {
			int outIndex = y;
			int ta = 0, tr = 0, tg = 0, tb = 0;
			for (int i = -r; i <= r; i++) {
				int rgb = in[inIndex + clamp(i, 0, width - 1)];
				ta += (rgb >> 24) & 0xff;
				tr += (rgb >> 16) & 0xff;
				tg += (rgb >> 8) & 0xff;
				tb += rgb & 0xff;
			}

			for (int x = 0; x < width; x++) {
				out[outIndex] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb];
				int i1 = x + r + 1;
				if (i1 > widthMinus1) {
					i1 = widthMinus1;
				}
				int i2 = x - r;
				if (i2 < 0) {
					i2 = 0;
				}
				int rgb1 = in[inIndex + i1];
				int rgb2 = in[inIndex + i2];
				ta += ((rgb1 >> 24) & 0xff) - ((rgb2 >> 24) & 0xff);
				tr += ((rgb1 & 0xff0000) - (rgb2 & 0xff0000)) >> 16;
				tg += ((rgb1 & 0xff00) - (rgb2 & 0xff00)) >> 8;
				tb += (rgb1 & 0xff) - (rgb2 & 0xff);
				outIndex += height;
			}
			inIndex += width;
		}
	}

	public static void blurFractional(int[] in, int[] out, int width, int height, float radius) {
		radius -= (int) radius;
		float f = 1.0f / (1 + 2 * radius);
		int inIndex = 0;
		for (int y = 0; y < height; y++) {
			int outIndex = y;
			out[outIndex] = in[0];
			outIndex += height;
			for (int x = 1; x < width - 1; x++) {
				int i = inIndex + x;
				int rgb1 = in[i - 1];
				int rgb2 = in[i];
				int rgb3 = in[i + 1];
				int a1 = (rgb1 >> 24) & 0xff;
				int r1 = (rgb1 >> 16) & 0xff;
				int g1 = (rgb1 >> 8) & 0xff;
				int b1 = rgb1 & 0xff;
				int a2 = (rgb2 >> 24) & 0xff;
				int r2 = (rgb2 >> 16) & 0xff;
				int g2 = (rgb2 >> 8) & 0xff;
				int b2 = rgb2 & 0xff;
				int a3 = (rgb3 >> 24) & 0xff;
				int r3 = (rgb3 >> 16) & 0xff;
				int g3 = (rgb3 >> 8) & 0xff;
				int b3 = rgb3 & 0xff;
				a1 = a2 + (int) ((a1 + a3) * radius);
				r1 = r2 + (int) ((r1 + r3) * radius);
				g1 = g2 + (int) ((g1 + g3) * radius);
				b1 = b2 + (int) ((b1 + b3) * radius);
				a1 *= f;
				r1 *= f;
				g1 *= f;
				b1 *= f;
				out[outIndex] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
				outIndex += height;
			}
			out[outIndex] = in[width - 1];
			inIndex += width;
		}
	}

	public static int clamp(int x, int a, int b) {
		return (x < a) ? a : (x > b) ? b : x;
	}


图片锐化效果

锐化与模糊是相反的,模糊要让景物的边缘变得模糊不清,而锐化则加强图像中景物的边缘和轮廓,从而让景物的边缘变得更加清晰和锐利。


下面是锐化效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第8张图片


下面是锐化效果的示例代码:
	// 图像锐化效果。拉普拉斯算子处理
	public static Bitmap sharpen(Bitmap bmp) {
		/*
		 * 锐化基本思想是加强图像中景物的边缘和轮廓,使图像变得清晰 而图像平滑是使图像中边界和轮廓变得模糊
		 * 拉普拉斯算子图像锐化 获取周围9个点的矩阵乘以模板9个的矩阵 卷积
		 * 拉普拉斯算子模板 { 0, -1, 0, -1, -5, -1, 0, -1, 0 } { -1, -1, -1, -1, 9, -1, -1, -1, -1 }
		 */
		int[] laplacian = new int[] { -1, -1, -1, -1, 9, -1, -1, -1, -1 };
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int pixColor = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int idx = 0;
		float alpha = 0.3F;// 图片透明度
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		// 图像处理
		for (int i = 1; i < height - 1; i++) {
			for (int k = 1; k < width - 1; k++) {
				idx = 0;
				newR = 0;
				newG = 0;
				newB = 0;
				for (int n = -1; n <= 1; n++) {  // 取出图像3*3领域像素
					for (int m = -1; m <= 1; m++) {  // n行数不变 m列变换
						pixColor = pixels[(i + n) * width + k + m];// 当前点(i,k)
						pixR = Color.red(pixColor);
						pixG = Color.green(pixColor);
						pixB = Color.blue(pixColor);
						// 图像像素与对应摸板相乘
						newR = newR + (int) (pixR * laplacian[idx] * alpha);
						newG = newG + (int) (pixG * laplacian[idx] * alpha);
						newB = newB + (int) (pixB * laplacian[idx] * alpha);
						idx++;
					}
				}
				newR = Math.min(255, Math.max(0, newR));
				newG = Math.min(255, Math.max(0, newG));
				newB = Math.min(255, Math.max(0, newB));
				// 赋值
				pixels[i * width + k] = Color.argb(255, newR, newG, newB);
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片黑白效果

黑白照片对于彩色照片来说,就好比黑白电视机之于彩色电视机,黑白照片只有灰度的深浅区别,而没有红绿蓝之分。


下面是黑白效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第9张图片


下面是效果的示例代码:
	// 图片黑白效果
	public static Bitmap black(Bitmap bmp) {
		int width = bmp.getWidth(); // 获取位图的宽
		int height = bmp.getHeight(); // 获取位图的高
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		int alpha = 0xFF << 24;
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				int grey = pixels[width * i + j];

				int red = ((grey & 0x00FF0000) >> 16);
				int green = ((grey & 0x0000FF00) >> 8);
				int blue = (grey & 0x000000FF);

				grey = (int) (red * 0.3 + green * 0.59 + blue * 0.11);
				grey = alpha | (grey << 16) | (grey << 8) | grey;
				pixels[width * i + j] = grey;
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片冰冻效果

冰冻算法对于每个像素来说,RGB值都是当前色素值减去其余两个色素之和,由于现实生活中的暖色调偏红偏黄(比如说肤色、肉色等等),因此计算出来的颜色就偏蓝了。说白了,冰冻效果就是对色调取反,原来是暖色调就变为冷色调,原来是冷色调就变为暖色调。


下面是冰冻效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第10张图片


下面是冰冻效果的示例代码:
	// 图片冰冻效果
	public static Bitmap ice(Bitmap bmp) {
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		int pixColor = 0;
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		int newColor = 0;
		int newR = 0;
		int newG = 0;
		int newB = 0;
		int[] pixels = new int[width * height];
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		for (int i = 0; i < height; i++) {
			for (int k = 0; k < width; k++) {
				// 获取前一个像素颜色
				pixColor = pixels[width * i + k];
				pixR = Color.red(pixColor);
				pixG = Color.green(pixColor);
				pixB = Color.blue(pixColor);
				// 红色
				newColor = pixR - pixG - pixB;
				newColor = newColor * 3 / 2;
				if (newColor < 0) {
					newColor = -newColor;
				}
				if (newColor > 255) {
					newColor = 255;
				}
				newR = newColor;
				// 绿色
				newColor = pixG - pixB - pixR;
				newColor = newColor * 3 / 2;
				if (newColor < 0) {
					newColor = -newColor;
				}
				if (newColor > 255) {
					newColor = 255;
				}
				newG = newColor;
				// 蓝色
				newColor = pixB - pixG - pixR;
				newColor = newColor * 3 / 2;
				if (newColor < 0) {
					newColor = -newColor;
				}
				if (newColor > 255) {
					newColor = 255;
				}
				newB = newColor;
				pixels[width * i + k] = Color.argb(255, newR, newG, newB);
			}
		}
		bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
		return bitmap;
	}


图片素描效果

现实生活中的素描画,都是一根根黑线条描绘出景物的轮廓,所以图片的素描算法,便是提取出景物的轮廓,然后用黑色线条画出来。


下面是素描效果的结果截图:
Android开发笔记(九十七)图片的特效处理_第11张图片


下面是素描效果的示例代码:
	// 图片素描效果
	public static Bitmap sketch(Bitmap bmp) {
		// 创建新Bitmap
		int width = bmp.getWidth();
		int height = bmp.getHeight();
		int[] pixels = new int[width * height]; // 存储变换图像
		int[] linpix = new int[width * height]; // 存储灰度图像
		Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		bmp.getPixels(pixels, 0, width, 0, 0, width, height);
		int pixColor = 0;
		int pixR = 0;
		int pixG = 0;
		int pixB = 0;
		// 灰度图像
		for (int i = 1; i < width - 1; i++) {
			for (int j = 1; j < height - 1; j++) {  // 拉普拉斯算子模板 { 0, -1, 0, -1, -5, -1, 0, -1, 0
				// 获取前一个像素颜色
				pixColor = pixels[width * j + i];
				pixR = Color.red(pixColor);
				pixG = Color.green(pixColor);
				pixB = Color.blue(pixColor);
				// 灰度图像
				int gray = (int) (0.3 * pixR + 0.59 * pixG + 0.11 * pixB);
				linpix[width * j + i] = Color.argb(255, gray, gray, gray);
				// 图像反向
				gray = 255 - gray;
				pixels[width * j + i] = Color.argb(255, gray, gray, gray);
			}
		}
		int radius = Math.min(width / 2, height / 2);
		int[] copixels = gaussBlur(pixels, width, height, 10, 10 / 3); // 高斯模糊
																		// 采用半径10
		int[] result = colorDodge(linpix, copixels); // 素描图像 颜色减淡
		bitmap.setPixels(result, 0, width, 0, 0, width, height);
		return bitmap;
	}

	// 高斯模糊
	public static int[] 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;
			}
		}
		return data;
	}

	// 颜色减淡
	public static int[] 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;
		}
		return baseColor;
	}

	private static int colorDodgeFormular(int base, int mix) {
		int result = base + (base * mix) / (255 - mix);
		result = result > 255 ? 255 : result;
		return result;
	}



点击下载本文用到的图片特效处理的工程代码



点此查看Android开发笔记的完整目录

你可能感兴趣的:(android开发,Android开发笔记)