Android图像处理

图像色彩处理


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

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

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

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

颜色矩阵A
每个像素点的颜色分量矩阵C

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

矩阵乘法运算

计算过程:
     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

原始矩阵

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

改变颜色偏移量

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

改变颜色系数

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

改变色光属性

系统封装了一个类—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.
     * <p>
     * <code>axis=0</code> correspond to a rotation around the RED color
     * <code>axis=1</code> correspond to a rotation around the GREEN color
     * <code>axis=2</code> correspond to a rotation around the BLUE color
     * </p>
     */
    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;
    }
一些常用的图像颜色处理矩阵
  • 灰度效果
灰度矩阵
灰度效果
  • 图像反转
图像反转矩阵
图像反转效果
  • 怀旧效果
怀旧矩阵

怀旧效果
  • 去色效果
去色矩阵
去色效果
像素点分析

可以通过改变每个像素点的具体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;
底片效果

—老照片效果:

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);
老照片效果

—浮雕效果:

B.r = C.r - B.r + 127;
B.g = C.g - B.g + 127;
B.b = C.b - B.b + 127;
浮雕效果

图形变换处理


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):
平移变换

矩阵变换:

平移变换矩阵
  • 旋转变换
    旋转变换即指一个点围绕一个中心旋转到一个新的点。当从P(x0,y0)点,以坐标原点O为旋转中心旋转到P(x,y)时,
旋转变换

可以得到:

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
水平错切
水平错切矩阵变换

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

垂直错切
垂直错切矩阵变换
像素块分析

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

public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,float[] verts, int vertOffset,  int[] colors, int colorOffset, Paint paint);
初始图像
扭曲图像

要使用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

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

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

参考资料:《Android群英传》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容