接续我的上一篇文章:http://blog.csdn.net/mingyueyixi/article/details/78534580
这次是针对安卓上实现Photoshop的黑白调整算法。
其实,实现的方式和java是一致的。只是bitmap比较坑爹一些,如果在循环体中使用bitmap.getPixel() 与 bitmap.setPixel(),将导致耗时大大增加。
警告,警告:
bitmap.getPixel() 与 bitmap.setPixel() 效率极低,循环体中无论如何都不要使用bitmap对象进行的一系列与像素相关的操作。代替的方案是使用像素数组获取
package com.lu.adog.util.image;
import android.graphics.Bitmap;
/**
* @author Yue
* @date 2017/11/17 17:42
*/
public class PSGray {
/**
* Photoshop 黑白算法,默认效果
*
* @param image
* @return 新的黑白化图片
*/
public static Bitmap createBlackWhiteImage(Bitmap image) {
return createBlackWhiteImage(image, null);
}
/**
* Photoshop 黑白算法,默认效果
*
* @param image
* @return 新的黑白化图片
* @radios 颜色通道配置,依次为红、黄、 绿、 青、 蓝、紫六个通道
*/
public static Bitmap createBlackWhiteImage(Bitmap image, float[] radios) {
int width = image.getWidth(); //获取位图的宽
int height = image.getHeight(); //获取位图的高
Bitmap result = Bitmap.createBitmap(width, height, image.getConfig());
int alpha = 0xff;
int r = 0;
int g = 0;
int b = 0;
int max = 0;
int min = 0;
int mid = 0;
int gray = 0;
float radioMax = 0;
float radioMaxMid = 0;
if (radios == null) {
// 红 黄 绿 青 蓝 紫
radios = new float[]{0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.8f};
}
int[] resultPixle = new int[width*height];
for (int i = 0; i < width; i++) {//一列列扫描
for (int j = 0; j < height; j++) {
gray = image.getPixel(i, j);//此段代码从bitmap中获取某个点的颜色,也将导致耗时大大增加。
alpha = gray >>> 24;
r = (gray >> 16) & 0x000000ff;
g = (gray >> 8) & 0x000000ff;
b = gray & 0x000000ff;
if (r >= g && r >= b) {
max = r;
radioMax = radios[0];
}
if (g >= r && g >= b) {
max = g;
radioMax = radios[2];
}
if (b >= r && b >= g) {
max = b;
radioMax = radios[4];
}
if (r <= g && r <= b) { // g+ b = cyan 青色
min = r;
radioMaxMid = radios[3];
}
if (b <= r && b <= g) {//r+g = yellow 黄色
min = b;
radioMaxMid = radios[1];
}
if (g <= r && g <= b) {//r+b = m 洋红
min = g;
radioMaxMid = radios[5];
}
mid = r + g + b - max - min;
// 公式:gray= (max - mid) * ratio_max + (mid - min) * ratio_max_mid + min
gray = (int) ((max - mid) * radioMax + (mid - min) * radioMaxMid + min);
gray = (alpha << 24) | (gray << 16) | (gray << 8) | gray;
// 2000x3500大图,耗时相差2~5倍左右
//bitmap在循环中设置像素点,这个操作会导致耗时严重,耗时7秒。4096x4096图耗时22秒
// result.setPixel(i, j, gray);
resultPixle[j*width+i] = gray;//直接改变数组,最后bitmap再设像素
}
}
result.setPixels(resultPixle,0,width,0,0,width,height);//最后bitmap再设像素
return result;
}
}
如果在循环体中,使用 bitmap.setPixel(i, j, gray);
直接改变某一点的像素,那么n次循环下来,会导致效率大大降低,耗时相差2~5倍左右。本来在安卓中使用java来处理图片就很慢了。。。
测试的结果:
使用上叙的方法,4096x4096图在无压缩的情况下,完全处理完毕,需要耗时22秒。测试用的是360手机。其中,三星手机也进行过测试,但由于内存限制,进行了缩放加载,缩放后图片有2000*2000以上,之后再处理,耗时同样严重,10秒以上。
所以,图片处理这种事,java上的处理是不给力的,尤其是在移动端这种东东身上。除非是进行类似模糊这种,可以进行缩小之后处理,最后放大,而不影响最终效果的运算。
经过很久很久的排查,我终于怀疑了这一段代码:
gray = bitmap.getPixel(i, j);
这一段代码从bitmap中获取某个点的颜色,这是继第一次优化后,仍然导致ps黑白化高达10秒,20秒的罪魁祸首。这个方法不能用,那么我们只能通过最原始的方式——从像素数组获取指定点的颜色。
完全优化后的代码:
package com.lu.adog.util.image;
import android.graphics.Bitmap;
import com.lu.adog.util.Logg;
/**
* @author Yue
* @date 2017/11/17 17:42
*/
public class PSGray {
/**
* Photoshop 黑白算法,默认效果
*
* @param image
* @return 新的黑白化图片
*/
public static Bitmap createBlackWhiteImage(Bitmap image) {
return createBlackWhiteImage(image, null);
}
/**
* Photoshop 黑白算法,默认效果
*
* @param image
* @return 新的黑白化图片
* @radios 颜色通道配置,依次为红、黄、 绿、 青、 蓝、紫六个通道
*/
public static Bitmap createBlackWhiteImage(Bitmap image, float[] radios) {
int width = image.getWidth(); //获取位图的宽
int height = image.getHeight(); //获取位图的高
Bitmap result = Bitmap.createBitmap(width, height, image.getConfig());
int alpha = 0xff;
int r = 0;
int g = 0;
int b = 0;
int max = 0;
int min = 0;
int mid = 0;
int gray = 0;
float radioMax = 0;
float radioMaxMid = 0;
if (radios == null) {
// 红 黄 绿 青 蓝 紫
radios = new float[]{0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.8f};
}
int[] resultPixle = new int[width * height];
image.getPixels(resultPixle, 0, width, 0, 0, width, height);
for (int i = 0; i < width; i++) {//一行行扫描
for (int j = 0; j < height; j++) {
gray = resultPixle[j * width + i];
// gray = image.getPixel(i,j);//此方法效率极低,不要出现在循环体中,否则将导致极度耗时
alpha = gray >>> 24;
r = (gray >> 16) & 0x000000ff;
g = (gray >> 8) & 0x000000ff;
b = gray & 0x000000ff;
if (r >= g && r >= b) {
max = r;
radioMax = radios[0];
}
if (g >= r && g >= b) {
max = g;
radioMax = radios[2];
}
if (b >= r && b >= g) {
max = b;
radioMax = radios[4];
}
if (r <= g && r <= b) { // g+ b = cyan 青色
min = r;
radioMaxMid = radios[3];
}
if (b <= r && b <= g) {//r+g = yellow 黄色
min = b;
radioMaxMid = radios[1];
}
if (g <= r && g <= b) {//r+b = m 洋红
min = g;
radioMaxMid = radios[5];
}
mid = r + g + b - max - min;
// 公式:gray= (max - mid) * ratio_max + (mid - min) * ratio_max_mid + min
gray = (int) ((max - mid) * radioMax + (mid - min) * radioMaxMid + min);
gray = (alpha << 24) | (gray << 16) | (gray << 8) | gray;
// 2000x3500大图,耗时相差2~5倍左右
//bitmap在循环中设置像素点,这个操作会导致耗时严重,耗时7秒。4096x4096图耗时22秒
// result.setPixel(i, j, gray);
resultPixle[j * width + i] = gray;//直接改变数组,最后bitmap再设像素
}
}
result.setPixels(resultPixle, 0, width, 0, 0, width, height);//最后bitmap再设像素
return result;
}
}
然后,对3000*4100的大图进行黑白化,耗时居然已经不到1秒了!
至此,大功告成。
最后再次警告:
bitmap.getPixel() 与 bitmap.setPixel() 效率极低,循环体中无论如何都不要使用bitmap对象进行的一系列与像素相关的操作。代替的方案是使用像素数组获取