1. 色相、饱和度和亮度调节

色光三原色(RGBA模型):
R — red (红)
G — green (绿)
B — blue (蓝)
A — alpha (透明度)

色相:物体的颜色。
饱和度:颜色的纯度,从0(灰)到100%(饱和)来描述。
亮度:颜色的相对明暗程度。

先修改主界面 activity_main.xml 布局,如图所示:

代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><TextView
        android:layout_marginBottom="40dp"android:layout_gravity="center_horizontal"android:text="图片处理"android:textSize="40sp"android:layout_width="wrap_content"android:layout_height="wrap_content" /><Button
        android:layout_width="match_parent"android:layout_height="wrap_content"android:text="PrimaryColor"android:onClick="primaryColorClick" /><Button
        android:layout_width="match_parent"android:layout_height="wrap_content"android:text="ColorMatrix"android:onClick="colorMatrixClick"/><Button
        android:layout_width="match_parent"android:layout_height="wrap_content"android:text="PixelsEffect"android:onClick="pixelsEffectClick"/>
</LinearLayout>

注意:在布局中引入 android:onClick 属性可以更方便的添加点击事件。但与点击事件的方法名要保持一致!

接下来,新建一个颜色处理的工具类 ImageHelper,封装对图片的操作,方便将来直接调用。代码如下:

public class ImageHelper {/*** 对传入的bitmap调整色光三原色(注意:传入的bitmap默认不可修改,不能直接处理,否则会报错)* @param bitmap: 传入的图片* @param hue: 色相* @param saturation: 饱和度* @param lum: 亮度* @return*/public static Bitmap handleImage(Bitmap bitmap, float hue, float saturation, float lum) {/*** 创建一个同样大小的图片* Bitmap.Config.ARGB_8888: 颜色模式,bitmap的最高处理方式*/Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);/*** 创建一张与传入的bitmap一样大小的画布(canvas),之后的操作都在画布上* ANTI_ALIAS_FLAG: 抗锯齿*/Canvas canvas = new Canvas(bmp);Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);/*** setRotate: 设置色相(RGB)* 0:RED* 1:GREEN* 2:BLUE*/ColorMatrix hurMatrix = new ColorMatrix();hurMatrix.setRotate(0, hue);hurMatrix.setRotate(1, hue);hurMatrix.setRotate(2, hue);/*** setSaturation: 设置饱和度*/ColorMatrix saturationMatrix = new ColorMatrix();saturationMatrix.setSaturation(saturation);/*** setScale: 设置亮度(4个参数分别为:r、g、b、透明度)*/ColorMatrix lumMatrix = new ColorMatrix();lumMatrix.setScale(lum, lum, lum, 1);/*** 综合前面设置几个属性*/ColorMatrix imageMatrix = new ColorMatrix();imageMatrix.postConcat(hurMatrix);imageMatrix.postConcat(saturationMatrix);imageMatrix.postConcat(lumMatrix);/*** 传入imageMatrix,画到新的画布上* 在canvas上绘制出图片*/paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));canvas.drawBitmap(bitmap, 0, 0, paint);// paint包含了前面设置的三个属性(色调、饱和度、亮度)return bmp;}
}

注意 bitmap 和 bmp 不要混淆。

新建一个布局,名为 primary_color.xml,用以显示图片和需要调节的参数。如图所示(代码附文末源码中):

新建 PrimaryColor 类,继承自 Activity,对 SeekBar 做相应的处理逻辑,

public class PrimaryColor extends Activity implements SeekBar.OnSeekBarChangeListener {private ImageView imageView;private SeekBar seekBarHue, seekBarSaturation, seekBarLum;public static final int MAX_VALUE = 255;public static final int MID_VALUE = 127;private float mHue, mSaturation, mLum;// 传递给图像处理工具类参数private Bitmap bitmap;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.primary_color);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.p3);// 将资源中的图片转化为bitmapimageView = (ImageView) findViewById(R.id.image);seekBarHue = (SeekBar) findViewById(R.id.seekBarHue);// 实例化seekBarSaturation = (SeekBar) findViewById(R.id.seekBarSaturation);seekBarLum = (SeekBar) findViewById(R.id.seekBarLum);seekBarHue.setOnSeekBarChangeListener(this);// 添加监听seekBarSaturation.setOnSeekBarChangeListener(this);seekBarLum.setOnSeekBarChangeListener(this);seekBarHue.setMax(MAX_VALUE);// 设置seekBar最大值seekBarSaturation.setMax(MAX_VALUE);seekBarLum.setMax(MAX_VALUE);seekBarHue.setProgress(MID_VALUE);// 设置初始值seekBarSaturation.setProgress(MID_VALUE);seekBarLum.setProgress(MID_VALUE);imageView.setImageBitmap(bitmap);// 程序初始化时显示原图}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {switch (seekBar.getId()) {case R.id.seekBarHue:mHue = (progress - MID_VALUE) * 1.0f / MID_VALUE * 180;// 色调变化公式(经验值,并非固定的)break;case R.id.seekBarSaturation:mSaturation = progress * 1.0f / MID_VALUE;// 饱和度变化公式break;case R.id.seekBarLum:mLum = progress * 1.0f / MID_VALUE;// 亮度变化公式break;}// 通过工具类ImageHelper处理图片,返回新的bitmapimageView.setImageBitmap(ImageHelper.handleImage(bitmap, mHue, mSaturation, mLum));}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}
}

在 MainActivity 中添加点击事件,跳转到 PrimaryColor,

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void primaryColorClick(View view) {startActivity(new Intent(this, PrimaryColor.class));}}

最后,由于添加了新的 Activity ( PrimaryColor ),需要在配置文件 AndroidManifest.xml 中对其进行注册:

<activity android:name=".PrimaryColor" />

接下来,运行程序,并点击 PrimaryColor 按钮,就跳转到如下界面:

拖动各个 SeekBar,就可以分别调节图片的色相、饱和度和亮度,看到图片的变化效果了。

2. 颜色矩阵

2.1 颜色矩阵知识

Android 中可以通过颜色矩阵(ColorMatrix 类)操作颜色。
颜色矩阵是一个 4*5 的矩阵(图1.1)。可以修改图片中RGBA各分量的值,颜色矩阵以一维数组的方式存储如下:
[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]

他通过 RGBA 四个通道来直接操作对应颜色,如果会使用 Photoshop 就会知道有时处理图片通过控制 RGBA 各颜色通道来做出特殊的效果。这个矩阵对颜色的作用计算方式如1.3示:

矩阵的运算规则:矩阵 A 的一行乘以矩阵 C 的一列作为矩阵 R 的一行。

矩阵 C 是图片中包含的 RGBA 信息,R 矩阵是用颜色矩阵应用于C之后的新的颜色分量,运算结果如下:

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;

颜色矩阵并不是看上去那么深奥,其实需要使用的参数很少,而且很有规律:
1. 第一行决定红色;
2. 第二行决定绿色;
3. 第三行决定蓝色;
4. 第四行决定了透明度;
5. 第五列是颜色的偏移量。

下面是一个实际中使用的颜色矩阵:

如果把这个矩阵作用于各颜色分量的话,R=A*C,计算后会发现,各个颜色分量实际上没有任何的改变(R’=R, G’=G, B’=B, A’=A)。


图1.5所示矩阵计算后会发现红色分量增加100,绿色分量增加100,这样的效果就是图片偏黄,因为红色和绿色混合后得到黄色,黄色增加了100,图片当然就偏黄了。

改变各颜色分量不仅可以通过修改第5列的颜色偏移量也可像下面矩阵那样将对应的颜色值乘以一个倍数,直接放大。图1.6就是将绿色分量乘以2变为原来的2倍。

2.2 实际应用

下面用一个例子来应用前面的知识点。

首先新建一个布局文件 color_matrix.xml,用以显示要修改的图片和 4*5 矩阵,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><ImageView
        android:layout_marginTop="20dp"android:id="@+id/image_view"android:layout_width="match_parent"android:layout_weight="3"android:layout_height="0dp" /><!-- 4行5列矩阵 --><GridLayout
        android:id="@+id/matrix"android:layout_width="match_parent"android:layout_weight="2"android:layout_height="0dp"android:rowCount="4"android:columnCount="5" ></GridLayout><LinearLayout
        android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><Button
            android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:text="Change"android:onClick="changeClick"/><Button
            android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:text="Reset"android:onClick="resetClick"/></LinearLayout></LinearLayout>

添加逻辑处理类 ColorMatrix,

public class ColorMatrix extends Activity {private ImageView imageView;private GridLayout matrix;private Bitmap bitmap;private int mWidth, mHeight;// 控件宽高private EditText[] editTexts = new EditText[20];// 保存这20个EditTextprivate float[] mColorMatrix = new float[20];// 保存20个EditText的值@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.color_matrix);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.p1);imageView = (ImageView) findViewById(R.id.image_view);matrix = (GridLayout) findViewById(R.id.matrix);imageView.setImageBitmap(bitmap);// 初始展示原图片/*** 用post方法在控件绘制完后获取控件宽高,初始化每一个EditText宽高,显示20个EditText*/matrix.post(new Runnable() {@Overridepublic void run() {mWidth = matrix.getWidth() / 5;mHeight = matrix.getHeight() / 4;addText();initMatrix();// 初始化矩阵}});}/*** 动态添加 4*5 矩阵参数(20个EditText)*/private void addText() {for (int i = 0; i < 20; i++) {EditText editText = new EditText(ColorMatrix.this);editTexts[i] = editText;// 初始化数组matrix.addView(editText, mWidth, mHeight);}}/*** 初始化矩阵:第0、6、12、18位设置为1,其他为0*/private void initMatrix() {for (int i = 0; i < 20; i++) {if (i % 6 == 0) {editTexts[i].setText(String.valueOf(1));}else {editTexts[i].setText(String.valueOf(0));}}}public void changeClick(View view) {getMatrix();// 获取当前ColorMatrix里EditText所有的值setImageMatrix();// 应用新的颜色矩阵到bitmap}/*** 赋值*/private void getMatrix() {for (int i = 0; i < 20; i++) {mColorMatrix[i] = Float.valueOf(editTexts[i].getText().toString());}}private void setImageMatrix() {Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);android.graphics.ColorMatrix colorMatrix = new android.graphics.ColorMatrix();colorMatrix.set(mColorMatrix);// 将一个数组传递进来,变为颜色矩阵Canvas canvas = new Canvas(bmp);Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);// 抗锯齿paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));canvas.drawBitmap(bitmap, 0, 0, paint);// 注意 bitmap 和 bmp别写错了imageView.setImageBitmap(bmp);// 处理后的图像}/*** 复位操作,回到初始状态*/public void resetClick(View view) {initMatrix();// 数据回到初始化getMatrix();setImageMatrix();// 还原图像}

在主函数中添加点击跳转事件:

 public void colorMatrixClick(View view) {startActivity(new Intent(this, ColorMatrix.class));}

最后,别忘了在 AndroidManifest.xml 注册。

运行程序,并点击 ColorMatrix 按钮,跳转到如下界面:

根据前面的颜色矩阵变换知识,在 4*5 矩阵输入不同的数值,并点击 Change 按钮即可对图片进行相应的操作。此外,点击 Reset 按钮可回到初始的效果。

3. 一些效果展示

有了之前的工具类 ImageHelper,就可以在里面添加各种处理效果了。

这里添加三种处理效果为例,分别显示底片、怀旧和浮雕效果,代码如下:

    /*** 底片效果,循环处理所有的像素点*/public static Bitmap handleImageNegative(Bitmap bitmap) {int width = bitmap.getWidth();int height = bitmap.getHeight();int color;int r, g, b, a;Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);int[] oldPx = new int[width * height];// 存储像素点的数组int[] newPx = new int[width * height];// 存储新像素点bitmap.getPixels(oldPx, 0, width, 0, 0, width, height);// 取出像素点for (int i = 0; i < width * height; i++) {color = oldPx[i];/*** 提取分离color中的r, g, b, a分量*/r = Color.red(color);g = Color.green(color);b = Color.blue(color);a = Color.alpha(color);/*** 底片效果算法*/r = 255 - r;g = 255 - g;b = 255 - b;// 判断是否越界if (r > 255) {r = 255;}else if (r < 0) {r = 0;}if (g > 255) {g = 255;}else if (g < 0) {g = 0;}if (b > 255) {b = 255;}else if (b < 0) {b = 0;}newPx[i] = Color.argb(a, r, g, b);// 合成新的颜色,赋给新的像素点}bmp.setPixels(newPx, 0, width, 0, 0, width, height);// 修改后的效果(注意方法为set)return bmp;}/*** 怀旧效果*/public static Bitmap handleImagePixelsOldPhoto(Bitmap bitmap) {Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),Bitmap.Config.ARGB_8888);int width = bitmap.getWidth();int height = bitmap.getHeight();int color = 0;int r, g, b, a, r1, g1, b1;int[] oldPx = new int[width * height];int[] newPx = new int[width * height];bitmap.getPixels(oldPx, 0, bitmap.getWidth(), 0, 0, width, height);for (int i = 0; i < width * height; i++) {color = oldPx[i];a = Color.alpha(color);r = Color.red(color);g = Color.green(color);b = Color.blue(color);/*** 怀旧效果算法*/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);if (r1 > 255) {r1 = 255;}if (g1 > 255) {g1 = 255;}if (b1 > 255) {b1 = 255;}newPx[i] = Color.argb(a, r1, g1, b1);}bmp.setPixels(newPx, 0, width, 0, 0, width, height);return bmp;}/*** 浮雕效果(注意:此处从第一个像素开始,因此循环从1开始)*/public static Bitmap handleImagePixelsRelief(Bitmap bitmap) {Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),Bitmap.Config.ARGB_8888);int width = bitmap.getWidth();int height = bitmap.getHeight();int color = 0, colorBefore = 0;int a, r, g, b;int r1, g1, b1;int[] oldPx = new int[width * height];int[] newPx = new int[width * height];bitmap.getPixels(oldPx, 0, bitmap.getWidth(), 0, 0, width, height);// 注意:此处循环从1开始for (int i = 1; i < width * height; i++) {colorBefore = oldPx[i - 1];a = Color.alpha(colorBefore);r = Color.red(colorBefore);g = Color.green(colorBefore);b = Color.blue(colorBefore);/*** 浮雕效果算法*/color = oldPx[i];r1 = Color.red(color);g1 = Color.green(color);b1 = Color.blue(color);r = (r - r1 + 127);g = (g - g1 + 127);b = (b - b1 + 127);if (r > 255) {r = 255;}if (g > 255) {g = 255;}if (b > 255) {b = 255;}newPx[i] = Color.argb(a, r, g, b);}bmp.setPixels(newPx, 0, width, 0, 0, width, height);return bmp;}

接下来新建一个类 PixelsEffect,继承自Activity,把这几种效果显示出来,代码如下:

imageView1.setImageBitmap(bitmap);// 显示原图
imageView2.setImageBitmap(ImageHelper.handleImageNegative(bitmap));// 底片效果
imageView3.setImageBitmap(ImageHelper.handleImagePixelsOldPhoto(bitmap));// 怀旧效果
imageView4.setImageBitmap(ImageHelper.handleImagePixelsRelief(bitmap));// 浮雕效果

最后别忘了在 AndroidManifest.xml 中注册。

运行程序,并点击 PixelsEffect 按钮,跳转到如下界面:四张图分别显示原图、底片效果、怀旧效果和浮雕效果。

这里只是一些最基本的处理,其他还有旋转、扭曲等许多操作需日后进一步学习。

PS: 个人感觉,学东西只有能用自己的话讲清楚怎么回事,才算学明白了。
现在还是囫囵吞枣,有待进一步消化理解……

主要内容整理总结自 慕课网
参考:http://www.cnblogs.com/leon19870907/articles/1978065.html

源码下载

Android 图片处理相关推荐

  1. android layout后还原位置,Android图片框架photoview如何记住所有状态并还原,包括缩放度,缩放后的移动的距离等等...

    Android图片框架photoview如何记住状态并还原,包括缩放度,缩放后的移动的距离等等,尝试了好多方法都没有作用. private void generateImages() { for (i ...

  2. Android 图片缓存之内存缓存技术LruCache,软引用

    Android 图片缓存之内存缓存技术LruCache,软引用

  3. Android图片压缩(质量压缩和尺寸压缩)

    在网上调查了图片压缩的方法并实装后,大致上可以认为有两类压缩:质量压缩(不改变图片的尺寸)和尺寸压缩(相当于是像素上的压缩):质量压缩一般可用于上传大图前的处理,这样就可以节省一定的流量,毕竟现在的手 ...

  4. Android图片缓存之Lru算法

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...

  5. Android图片缓存框架Glide

    Android图片缓存框架Glide Glide是Google提供的一个组件.它具有获取.解码和展示视频剧照.图片.动画等功能.它提供了灵活的API,帮助开发者将Glide应用在几乎任何网络协议栈中. ...

  6. Android图片处理

    相信做Android开发的小伙伴对于Android图片压缩.裁剪一定有很深的印象,今天我将带领大家一起学习一下这个看着高深莫测的知识,以便再以后的学习.工作中可以帮助到大家. 首先我们看一下这个问题出 ...

  7. Android图片剪裁库

    最近利用一周左右的业余时间,终于完成了一个Android图片剪裁库,核心功能是根据自己的理解实现的,部分代码参考了Android源码的图片剪裁应用.现在将该代码开源在Github上以供大家学习和使用, ...

  8. Android --- 图片的特效处理

    Android --- 图片处理的方法 转换 -  drawable To  bitmap 缩放 -  Zoom 圆角 -  Round Corner 倒影 -  Reflected bitmapPr ...

  9. Android 图片合成:添加蒙板效果 不规则相框 透明度渐变效果的实现

    Android 图片合成:添加蒙板效果 不规则相框 透明度渐变效果的实现 暂时还未有时间开发这效果,所以先贴出来. 先贴一张效果图,这是一张手机截屏: 左上方的风景图:背景图片 右上方的人物图:前景图 ...

  10. android富文本图片自适应,Android 图片混排富文本编辑器控件

    一.一个Android 图片混排富文本编辑器控件(仿兴趣部落) 1.1 图片混排富文本控件 是一种图片和文字混合在一起的控件,文本之间可以插入图片,类似于网页的排版样式. 1.2 该控件主要是仿兴趣部 ...

最新文章

  1. 服务器配置—开网站空间
  2. Arduino可穿戴教程ArduinoIDE新建编辑源文件
  3. SDUTOJ3771_数组计算机(线段树)
  4. Html 教程 (6)script标签
  5. ExcelBDD-Java开源组件发布了!
  6. Git完整入门教程(从0开始)
  7. AE开发中关于 “无法嵌入互操作类型.........请改用适用的接口”问题的解决方法...
  8. 移动超级sim卡 无法下载卡_中国移动发布超级SIM卡:全变了
  9. 速读训练软件_记忆宫殿记忆力训练教程-第八天
  10. python判断点在矩形内_定义一个矩形和点的位置,判断点是否在矩形里面
  11. java虚拟机手动内存分配_《深入理解java虚拟机》-垃圾收集器与内存分配策略
  12. JMeter设置集合点
  13. 微信小程序源码打包合集 游戏商城抽奖转盘预约点餐等-1
  14. Unity3DAI行为------路径跟随
  15. 离线 维基百科 android,iPhone上的离线维基百科(附安装方法)
  16. java 拟合曲线_关于java实现自定义曲线拟合的研究
  17. HTML的文本格式标记
  18. office2016镜像安装详细教程
  19. 自强不息,厚德载物——2021年年度总结
  20. Java多线程系列-CyclicBarrier

热门文章

  1. serializers.serialize
  2. iPhone手机热点连接不稳定
  3. 学术新秀采访-陆品燕~How To Get Your SIGGRAPH Paper Rejected
  4. 二分查找(整数二分)
  5. 基于单层决策树的adaBoost算法思想分析和源代码解析
  6. C语言冒泡排序(从大到小排序核心代码)
  7. NotBlank问题解决
  8. 神经网络 深度神经网络,深度神经网络训练
  9. 初识ELF格式 ABI,EABI,OABI
  10. 转:瑞利信道,莱斯信道和高斯信道模型