Android图像处理

图像色彩处理


Android对图片的处理,通常用到的数据结构就是位图—Bitmap,它包含了一张图片的所有数据。整个图片都是由点阵和颜色值组成的,点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素,颜色值—ARGB,分别对应透明度、红、绿、蓝这四个通道分量,它们共同决定了每个像素点显示的颜色,对图片的色彩处理实际上就是对这些像素点的通道分量做调整。

在色彩处理中,通常从三个角度来描述一个图像。

  • 色调:色彩的总体倾向
  • 饱和度:颜色的纯度,从0(灰)到100%(饱和)来进行描述
  • 亮度:颜色的相对明暗程度

在android中,系统使用一个颜色矩阵—ColorMatrix,来处理图像的这些色彩效果。Android中颜色矩阵是一个4x5的矩阵,它用来对图片的色彩进行处理。而对于每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值,如下图所示:

Android图像处理_第1张图片
颜色矩阵A
每个像素点的颜色分量矩阵C

在处理图像时,使用矩阵乘法运算AC来处理颜色分量矩阵,如下图所示:

Android图像处理_第2张图片
矩阵乘法运算

计算过程:
     R1 = a * R + b * G + c * B + d * A + e;
     G1 = f * R + g * G + h * B + i * A + j;
     B1 = k * R + l * G + m * B + n * A + o;
     A1 = p * R + q * G + r * B + s * A + t;

可以发现,对于颜色矩阵A是按以下方式划分的:
    * 第一行的a b c d e值决定新的颜色值中的R分量—红色
    * 第二行的f g h i j值决定新的颜色值中的G分量—绿色
    * 第三行的k l m n o值决定新的颜色值中的B分量—蓝色
    * 第四行的p q r s t值决定新的颜色值中的A分量—透明度
    * 矩阵A中的第五列—e j o t值分别决定每个分量中的offset,即偏移量

想要对原图片进行颜色的调整,就需要设置好用于调整颜色的矩阵A

Android图像处理_第3张图片
原始矩阵

通常有两种方法:
1、改变偏移量
将矩阵A的第五列的值进行修改,即改变颜色的偏移量,其他值保持初始矩阵的值

Android图像处理_第4张图片
改变颜色偏移量

  原图片每个像素点的矩阵红色和绿色的颜色分量都增加了100,红绿混合为黄色,最终会使得整张图片偏黄。
2、改变颜色系数
修改颜色分量中的某个系数值,其他值依然保持初始矩阵的值

Android图像处理_第5张图片
改变颜色系数

  矩阵运算后,原图片每个像素点的矩阵绿色的颜色分量会变为原来的两倍,最终使得原图片的色调偏绿。

改变色光属性

系统封装了一个类—ColorMatrix,通过这个类,可以很方便地通过改变矩阵值来处理颜色效果(色调、饱和度、亮度)。本质上是一个一维数组[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]。

ColorMatrix

调节色调(色彩的旋转运算):
  ColorMatrix类提供了setRotate(int axis, float degrees)来调节颜色的色调。第一个参数,使用0、1、2来代表Red、Green、Blue三种颜色的处理,第二个参数,就是需要处理的值。

    /**
     * Set the rotation on a color axis by the specified values.
     * 

* axis=0 correspond to a rotation around the RED color * axis=1 correspond to a rotation around the GREEN color * axis=2 correspond to a rotation around the BLUE color *

*/ public void setRotate(int axis, float degrees) { reset(); double radians = degrees * Math.PI / 180d; float cosine = (float) Math.cos(radians); float sine = (float) Math.sin(radians); switch (axis) { // Rotation around the red color case 0: mArray[6] = mArray[12] = cosine; mArray[7] = sine; mArray[11] = -sine; break; // Rotation around the green color case 1: mArray[0] = mArray[12] = cosine; mArray[2] = -sine; mArray[10] = sine; break; // Rotation around the blue color case 2: mArray[0] = mArray[6] = cosine; mArray[1] = sine; mArray[5] = -sine; break; default: throw new RuntimeException(); } }

调节饱和度
  通过色彩的平移运算单独增强R,G,B的饱和度,ColorMatrix类提供了setSaturation(float sat)方法来整体调节图像的饱和度,参数代表设置颜色饱和度的值,当饱和度为0时,图像变成灰度图像,数值越大图像越饱和。

    /**
     * Set the matrix to affect the saturation of colors.
     *
     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
     */
    public void setSaturation(float sat) {
        reset();
        float[] m = mArray;

        final float invSat = 1 - sat;
        final float R = 0.213f * invSat;
        final float G = 0.715f * invSat;
        final float B = 0.072f * invSat;

        m[0] = R + sat; m[1] = G;       m[2] = B;
        m[5] = R;       m[6] = G + sat; m[7] = B;
        m[10] = R;      m[11] = G;      m[12] = B + sat;
    }

调节亮度(色彩的缩放运算)
  当三原色以相同的比例进行混合的时候,就会显示出白色,使用这个原理来改变一个图像的亮度,亮度为0时,图像变为全黑。ColorMatrix类提供setScale(float rScale, float gScale, float bScale, float aScale)方法来调节颜色的亮度值。

    /**
     * Set this colormatrix to scale by the specified values.
     */
    public void setScale(float rScale, float gScale, float bScale,
                         float aScale) {
        final float[] a = mArray;

        for (int i = 19; i > 0; --i) {
            a[i] = 0;
        }
        a[0] = rScale;
        a[6] = gScale;
        a[12] = bScale;
        a[18] = aScale;
    }
一些常用的图像颜色处理矩阵
  • 灰度效果
Android图像处理_第6张图片
灰度矩阵
Android图像处理_第7张图片
灰度效果
  • 图像反转
Android图像处理_第8张图片
图像反转矩阵
Android图像处理_第9张图片
图像反转效果
  • 怀旧效果
Android图像处理_第10张图片
怀旧矩阵

Android图像处理_第11张图片
怀旧效果
  • 去色效果
Android图像处理_第12张图片
去色矩阵
Android图像处理_第13张图片
去色效果
像素点分析

可以通过改变每个像素点的具体ARGB值,来达到处理一张图像效果的目的。系统提供了Bitmap.getPixel()方法来获取某个像素点,也提供了Bitmap.getPixels()方法来提取整个Bitmap中的像素点,并保存到一个数组中:
getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
当获取到具体的颜色值之后,就可以通过相应的算法来修改它的ARGB值,从而重构到一张新的图像。

常用图像像素点处理效果

—底片效果:

B.r = 255 - B.r; 
B.g = 255 - B.g; 
B.b = 255 - B.b;
Android图像处理_第14张图片
底片效果

—老照片效果:

r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);
g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);
b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);
Android图像处理_第15张图片
老照片效果

—浮雕效果:

B.r = C.r - B.r + 127;
B.g = C.g - B.g + 127;
B.b = C.b - B.b + 127;
Android图像处理_第16张图片
浮雕效果

图形变换处理


Android系统对于图像的图形变换也是通过矩阵来进行处理的,每个像素点都表达了其坐标的X、Y信息,用于图形变换的矩阵是一个3x3的矩阵:

图形变换矩阵A
像素点坐标矩阵C

使用变换矩阵去处理每一个像素点的时候,与颜色矩阵的矩阵乘法一样:
    X1 = a * X + b * Y + c
    Y1 = d * X + e * Y + f
     1 = g * X + h * Y + i
通常情况下,会让g = h = 0,i = 1,这样就使1 = g * X + h * Y + i恒成立。
与色彩变换矩阵的初始矩阵一样,图形变换矩阵也有一个初始矩阵:

图形变换初始矩阵
图像的变形处理通常包含以下基本变换
  • 平移变换
    平移变换的坐标值变换过程就是将每个像素点都进行平移变换,从P(x0,y0)平移到P(x1,y1):
Android图像处理_第17张图片
平移变换

矩阵变换:

平移变换矩阵
  • 旋转变换
    旋转变换即指一个点围绕一个中心旋转到一个新的点。当从P(x0,y0)点,以坐标原点O为旋转中心旋转到P(x,y)时,
Android图像处理_第18张图片
旋转变换

可以得到:

x0 = r*cosα 
y0 = r*sinα 
x = r*cos(α+θ) = r*cosα*cosθ − r*sinα*sinθ = x0*cosθ − y0*sinθ 
y = r*sin(α+θ) = r*sinα*cosθ + r*cosα*sinθ = y0*cosθ + x0*sinθ

矩阵变换如下:

旋转矩阵变换

以上是以坐标原点为旋转中心进行旋转变换,如果以任意点O为旋转中心来进行旋转变换,通常需要以下三个步骤:
  1、将坐标原点平移到O点
  2、使用前面讲的以坐标原点为中心的旋转方法进行旋转变换
  3、将坐标原点还原

  • 缩放变换
    像素点是不存在缩放的概念,但是由于图像是由很多个像素点组成的,如果将每个点的坐标都进行相同比例的缩放,最终就会形成让整个图像缩放的效果
    x1 = K1 * x0
    y1 = K2 * y0
缩放矩阵变换
  • 错切变换
    错切变换是一种比较特殊的线性变换,错切变换的效果就是让所有点的X坐标(或者Y坐标)保持不变,而对应的Y坐标(或者X坐标)则按比例发生平移,且平移的大小和该点到Y轴(或者X轴)的距离成正比。错切变换通常包含两种——水平错切与垂直错切。
    水平错切:
    x1 = x0 + K1 * y0
    y1 = y0
Android图像处理_第19张图片
水平错切
水平错切矩阵变换

垂直错切:
  x1 = x0
  y1 = K2 * x0 + y0

Android图像处理_第20张图片
垂直错切
垂直错切矩阵变换
像素块分析

和图像的色彩处理有两种方式一样,图像的变形处理也有使用矩阵和像素块分析两种方式,drawBitmapMesh()与操纵像素点来改变色彩的原理类似,是把图像分成了一个个的小块,然后通过改变每一个图像块来修改整个图像。

public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,float[] verts, int vertOffset,  int[] colors, int colorOffset, Paint paint);
Android图像处理_第21张图片
初始图像
Android图像处理_第22张图片
扭曲图像

要使用drawBitmapMesh()方法就需先将图片分割为若干个图像块。在图像上横纵各画N条线,而这横纵各N条线就交织成了NxN个点,而每个点的坐标则以x1,y1,x2,y2,...,xn,yn的形式保存在verts数组中,也就是说verts数组的每两位用来保存一个交织点,第一个是横坐标,第二个是纵坐标。而整个drawBitmapMesh()方法改变图像的方式,就是靠这些坐标值的改变来重新定义每一个图像块,从而达到图像效果处理的功能。使用这个方法可以实现很多图像特效如旗帜飞扬、水波纹等效果。

一些开源图像处理库


  • GPUImage for Android

GPUImage 是iOS下一个开源的基于GPU的图像处理库,提供各种各样的图像处理滤镜,并且支持照相机和摄像机的实时滤镜。GPUImage for Android是它在Android下的实现,同样也是开源的。其中提供了几十多种常见的图片滤镜API,且其机制是基于GPU渲染,处理速度相应也比较快,是一个不错的图片实时处理框架。
GitHub地址:https://github.com/CyberAgent/android-gpuimage

  • ImageFilterForAndroid

支持一百多种图片处理效果

  • OpenCV

OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。

参考资料:《Android群英传》

你可能感兴趣的:(Android图像处理)