一、前期基础知识储备
UI掌握PS这一逆天的软件,可以实现将图片转化为素描或者水彩的效果,以素描为例:
- 在Photoshop中打开一张人物照片,按下快捷键“Ctrl+Shift+U”,把它转换成黑白颜色;
- 复制图层,得到一个副本图层。按下快捷键“Ctrl+I”,将副本图层转换成负片效果;
- 将副本图层下拉菜单选为“颜色减淡”,这时图片会亮得几乎什么也看不见,不要急,慢慢来;
- 在“滤镜”菜单下选择“模糊→高斯模糊”,模糊半径值可根据你需要的素描线条粗细深浅来设置。到此素描画像工作就完成了。
我们将以上四步进行抽象,得到将图片转为素描效果的步骤即为:
- 去色,将图片变为灰度图,即黑白图;
- 反相,得到每个像素的补色,具体效果就像照片的底片;
- 高斯模糊,把反相后的像素值平均一下;
- 颜色减淡,将第1步中的像素和第3步得到的像素值进行计算。
在Android图像处理领域,我们可以使用像素点分析的方法实现上述的效果组合。下面,用代码实现上述过程
二、上代码,具体实现素描算法
1)去色,获取黑白图;
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;
}
2)反相,得到图片的底图;
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;
}
3)高斯模糊,得到反高斯图像;
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;
}
}