这篇博客将会通过对像素的RGB分量做一个处理,然后达到一些特效。并没有很高端大气的代码。也没用使用ImageFilter等一些库。多数参考了别人,大神勿喷。
首先看一下今天的效果图。

由于上传大小限制的关系,只有一小部分。当然,功能中除了光晕,其他都是实现了的。如果可以的话,我回上传到github上gif图。代码请在文末下载。那么接下来,我们就来看下如何实现这些。
再次声明,绝大多数是操作像素实现的。速度上可能会很慢。不过不要紧,要的是思想。

由于代码太多的原因,下面只会给出关键性代码,更多代码请前往github。

  • 图片灰度化
    灰度化原理:当前像素值=0.3r+0.59g+0.11b
    灰度化我在这里用2中方法实现的。一种是操作ColorMatrix,另一种是颜色分量处理。代码如下
public Bitmap doPro(Bitmap src) {        int width,height;        height = src.getHeight();        width = src.getWidth();        Bitmap bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(bitmap);        Paint paint = new Paint();        ColorMatrix colorMatrix = new ColorMatrix();        colorMatrix.setSaturation(0);        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);        paint.setColorFilter(filter);        canvas.drawBitmap(src, 0, 0, paint);        return bitmap;    }

关于ColorMatrix,参考官方文档。

@Override    public Bitmap doProByPix(Bitmap src) {        int width = src.getWidth();        int height = src.getHeight();        //创建像素点数组        int[] pixels = new int[width*height];        int alpha,grey,red,green,blue;        src.getPixels(pixels,0,width,0,0,width,height);        alpha = 0xFF<<24;        for (int i = 0 ; i < height ; i++){            for (int j = 0 ; j < width ; j++){                grey  = pixels[width*i+j];                red = ((grey & 0x00FF0000)>>16);                green = ((grey & 0x0000FF00)>>8);                blue = ((grey & 0x000000FF));                grey = (int)((float)red*0.3+(float)green*0.59+(float)blue*0.11);                grey = alpha | (grey<<16)|(grey<<8)|grey;                pixels[width*i+j]=grey;            }        }        Bitmap pro = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);        pro.setPixels(pixels,0,width,0,0,width,height);        return pro;    }

上面这个通过对颜色的 与,左移,右移来拿到颜色分量,当然,也有更简单的方法拿到分离,后面会说。

  • 对亮度/饱和度/对比度的操作
    这里的操作就会用到颜色矩阵,颜色矩阵,我并不会多说。如果感兴趣就取查看官方文档。这并不是重点
public Bitmap doPro(Bitmap src) {        int width = src.getWidth();        int height = src.getHeight();        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);        ColorMatrix colorMatrix = new ColorMatrix();        //设置饱和度 设置为0.7  范围 0 - 1        //colorMatrix.setSaturation((float) 0.7);        //设置亮度        colorMatrix.set(new float[]{1,0,0,0,70,                                    0,1,0,0,70,                                    0,0,1,0,70,                                    0,0,0,1,0});        //改变对比度//        colorMatrix.set(new float[]{2, 0, 0, 0, 0,//                                    0, 2, 0, 0, 0,//                                    0, 0, 2, 0, 0,//                                    0, 0, 0, 1, 0});        Paint paint = new Paint();        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        Canvas canvas = new Canvas(bitmap);        canvas.drawBitmap(src,0,0,paint);        return bitmap;    }
  • 水印文字
    就是在图片之上绘制文字,详情看代码。
  • 怀旧效果的实现
    原理:

R=0.393r+0.769g+0.189b
G=0.349r+0.686g+0.168b
B=0.272r+0.534g+0.131b

怀旧效果也可以通过颜色矩阵来实现,下面是通过颜色矩阵实现的部分代码

colorMatrix.set(new float[]{                (float) 0.393, (float) 0.768, (float) 0.189, 0, 0,                (float) 0.349, (float) 0.686, (float) 0.168, 0, 0,                (float) 0.272, (float) 0.534, (float) 0.131, 0, 0,                0, 0, 0, 1, 0        });

接下来我们看如何通过操作像素分量来实现

public Bitmap doProByPix(Bitmap src) {        /**         * 怀旧效果原理         * R=0.393r+0.769g+0.189b         * G=0.349r+0.686g+0.168b         * B=0.272r+0.534g+0.131b         */        long startTime = System.currentTimeMillis();        int width = src.getWidth();        int height = src.getHeight();        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);        int pixColor,pixR,pixG,pixB,newR,newG,newB;        int[] pixels= new int[width*height];        src.getPixels(pixels, 0, width, 0, 0, width, height);        for (int i = 0; i < height; i++) {            for (int j = 0; j < width; j++) {                //获取对应点的像素                pixColor  = pixels[width*i+j];                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,Math.min(255,newR),Math.min(255,newG),Math.min(255, newB));                pixels[width*i+j]=newColor;            }        }        bitmap.setPixels(pixels,0,width,0,0,width,height);        long endTime = System.currentTimeMillis();        Log.e("tag","this is old used time "+(endTime-startTime)+"ms");        return bitmap;    }

代码都很简单,而且相同部分很多,重点就在于颜色分量的处理,这个处理是根据原理 来的。

  • 模糊效果
    原理:周边像素平均值
    高斯模糊原理:周边像素的加权平均值

1.通过RenderScript来实现模糊

public Bitmap RSblur(Bitmap src){        long startTime = System.currentTimeMillis();        Bitmap bitmap = Bitmap.createBitmap(src.getWidth(),src.getHeight(), Bitmap.Config.ARGB_8888);        RenderScript renderScript = RenderScript.create(MyApplication.app);        ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));        Allocation allIn = Allocation.createFromBitmap(renderScript, src);        Allocation allOut = Allocation.createFromBitmap(renderScript,bitmap);        // 控制模糊程度        scriptIntrinsicBlur.setRadius(2.f);        scriptIntrinsicBlur.setInput(allIn);        scriptIntrinsicBlur.forEach(allOut);        allOut.copyTo(bitmap);        src.recycle();        renderScript.destroy();        long endTime = System.currentTimeMillis();        Log.e("tag","this is RenderScript blur use time "+(endTime-startTime)+"ms");        return bitmap;    }

2.操作像素RGB
这里我用了网上的一个FastBlur类,这个类实现了模糊并且做了优化。

  • 锐化效果
    原理:分别获取当前像素点和八个周围像素点的RGB值,先求出当前像素点的RGB值与八个像素点RGB值的和的平均数,再乘以相应的系数,然后在与当前像素点之和
pixColor = pixels[(i + n) * width + k + m];                        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++;

这里利用拉普拉斯矩阵。

  • 浮雕效果
    原理:前一像素分量值-当前像素分量值+127
    核心代码:
oldColor=oldPixels[i-1];            oldPixR = Color.red(oldColor);            oldPixG = Color.green(oldColor);            oldPixB = Color.blue(oldColor);            newColor = newPixels[i];            newPixR = Color.red(newColor);            newPixG = Color.green(newColor);            newPixB = Color.blue(newColor);            //计算            newPixR = (oldPixR-newPixR +127)>255?255:(oldPixR-newPixR +127) ;            newPixG = (oldPixG -newPixG+127)>255?255:(oldPixG -newPixG+127) ;            newPixB = (oldPixB-newPixB+127)>255?255:(oldPixB-newPixB+127);
  • 底片效果
    原理:取当前RGB分量与255差值作为最终分量值
    代码:
color = piexls[i];            pixR = 255 - Color.red(color);            pixG = 255 - Color.green(color);            pixB = 255 - Color.blue(color);
  • 光照效果
    原理,以指定点为中心,置顶光照半径,计算点到光照中心的距离,根据距离增加光照值

核心代码:

color = pixels[j*width+i];                pixR= Color.red(color);                pixG= Color.green(color);                pixB = Color.blue(color);                int distance = (int) (Math.pow((centerY-j),2)+Math.pow((centerX-i),2));                if (distance*radius);                {                    //按照距离大小计算增强的光照值                    int result = (int) (strength*(1.0-Math.sqrt(distance)/radius));                    pixR = pixR+result;                    pixG = pixG+result;                    pixB = pixB+result;                }                //做左右限制                pixR = Math.min(255,Math.max(0,pixR));                pixG = Math.min(255,Math.max(0,pixG));                pixB = Math.min(255,Math.max(0,pixB));
  • 熔铸效果
    原理:当前RGB分量*128然后对(其他2个分量之和+1)取整,并保留分量的修改
    核心代码:
color=pixels[i];            pixR = Color.red(color);            pixG = Color.green(color);            pixB = Color.blue(color);            //R 分量            pixR = pixR * 128/(pixG+pixB+1);            pixR = Math.min(255,Math.max(0,pixR));            //G 分量            pixG = pixG*128/(pixB+pixR+1);            pixG =Math.min(255,Math.max(0,pixG));            //B 分量            pixB = pixB*128/(pixR+pixG+1);            pixB = Math.min(255,Math.max(0,pixB));
  • 冰冻效果
    原理:分量值 = (当前分量值-其他2个分量值的绝对值)*3/2;
    核心代码:
color = pixels[i];            pixR = Color.red(color);            pixG = Color.green(color);            pixB = Color.blue(color);            pixR = Math.abs((pixR - pixG - pixB) * 3 / 2);            pixR = Math.min(255, pixR);            pixG = Math.abs((pixG - pixR - pixB) * 3 / 2);            pixG = Math.min(255, pixG);            pixB = Math.abs((pixB - pixR - pixG) * 3 / 2);            pixB = Math.min(255,pixB);
  • 雾化效果
    原理:在图像中引入一定的随机值,打乱图像中的像素值
    核心代码:
k = random.nextInt(123456);                int dx = i+k%8;                int dy = j+k%8;                if (dx>=width){                    dx=width-1;                }                if (dy>=height){                    dy=height-1;                }                pos = dy*width +dx;                pos1 = j*width+i;                pixels[pos1]=pixels[pos];
  • 积木效果
    原理:取3分量平均值,若大于128,则取255,否则取0 作为新分量值
    核心代码:
color = pixels[i];            sum = (Color.red(color)+Color.blue(color)+Color.green(color))/3;            if (sum>=128){                sum = 255;            }else{                sum = 0;            }            pixels[i]=Color.argb(Color.alpha(color),sum,sum,sum);
  • 连环画效果
    原理:
    R=|g-b+g+r|*r/256
    G=|b-g+b+r|*r/256
    B=|b-g+b+r|*g/256
    核心代码
color=pixels[i];            pixR= Color.red(color);            pixG = Color.green(color);            pixB = Color.blue(color);            //r            pixR = Math.abs(pixG-pixB+pixG+pixR) * pixR /256;            if (pixR>255){                pixR = 255;            }            pixG =Math.abs(pixB-pixG+pixB+pixR) *pixR/256;            if (pixG>255){                pixG = 255;            }            //B=|b-g+b+r|*g/256            pixB = Math.abs(pixB-pixG+pixB+pixR) * pixG/256;            if (pixB>256){                pixB=255;            }
  • 边缘高亮效果(霓虹处理)
    原理:计算原像素的RGB分量与相同行(i+1)以及相同列(j+1)相邻像素的梯度,就是差的平方和的平方根,然后将梯度值作为处理后的像素
    核心代码:
color=pixels[j*width+i];                //获取i+1像素                color_right = pixels[j*width+i+1];                //获取j+1像素                color_bottom = pixels[(j+1)*width+i];                //计算R分量                pixR = (int) (Math.pow((Color.red(color)-Color.red(color_right)),2)                        +Math.pow((Color.red(color)-Color.red(color_bottom)),2));                pixR = ((int) (Math.sqrt(pixR)*2));                pixR = Math.min(255,Math.max(0,pixR));                //计算G 分量                pixG = (int) (Math.pow((Color.green(color)-Color.green(color_right)),2)                        +Math.pow((Color.green(color)-Color.green(color_bottom)),2));                pixG = ((int) (Math.sqrt(pixG)*2));                pixG = Math.min(255,Math.max(0,pixG));                //计算B分量                pixB = (int) (Math.pow((Color.blue(color)-Color.blue(color_right)),2)                        +Math.pow((Color.blue(color)-Color.blue(color_bottom)),2));                pixB = ((int) (Math.sqrt(pixB)*2));                pixB = Math.min(255,Math.max(0,pixB));                pixels[j*width+i] = Color.argb(Color.alpha(color),pixR,pixG,pixB);

到这里就结束了,当然,图片处理特效还有很多,我这里仅仅是一小部分。在这里推荐一个库:ImageFilter

代码地址:github
参考资料:落日小屋
参考资料:其他原理

更多相关文章

  1. 开发者大杀器 —— Battery Historian,刨根问底,揪出 Android(安卓
  2. 编写高效的Android代码 .
  3. android和javaEE更完美的通信-传递对象
  4. [置顶] Android之场景桌面(一)
  5. Glide源码解析篇之框架主体结构(一)
  6. Android(安卓)Apk瘦身指南大全
  7. [置顶] 浅析android中的权限管理--用户安装的apk的uid,gid是如何
  8. 包括后台的Android美食APP项目开源代码,androidapp
  9. (原)android的JNI中使用C++的类

随机推荐

  1. 【故障】dell G7 7588笔记本已安装声卡驱
  2. 码农之路(2)- 曲折的求职之路
  3. 码农之路 - 楔子
  4. K8S node(节点)
  5. 【我的Linux,我做主!】kubernetes基础概念
  6. Linux下安装配置Cntlm代理
  7. Docker快速搭建Clickhouse集群(3分片3副
  8. 联想g510电脑安装无线网卡bcm43142
  9. Java 对象的哈希值是每次 hashCode() 方
  10. 码农之路(8)- 高光时刻