Android 图片处理
2015-06-07 10:15
686 查看
1. 色相、饱和度和亮度调节
色光三原色(RGBA模型):R — red (红)
G — green (绿)
B — blue (蓝)
A — alpha (透明度)
色相:物体的颜色。
饱和度:颜色的纯度,从0(灰)到100%(饱和)来描述。
亮度:颜色的相对明暗程度。
先修改主界面 activity_main.xml 布局,如图所示:
代码如下:
[code]<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,封装对图片的操作,方便将来直接调用。代码如下:
[code]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 做相应的处理逻辑,
[code]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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.primary_color); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.p3);// 将资源中的图片转化为bitmap imageView = (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);// 程序初始化时显示原图 } @Override public 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处理图片,返回新的bitmap imageView.setImageBitmap(ImageHelper.handleImage(bitmap, mHue, mSaturation, mLum)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }
在 MainActivity 中添加点击事件,跳转到 PrimaryColor,
[code]public class MainActivity extends Activity { @Override protected 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 中对其进行注册:
[code]<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 矩阵,代码如下:
[code]<?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,
[code]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个EditText private float[] mColorMatrix = new float[20];// 保存20个EditText的值 @Override protected 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() { @Override public 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();// 还原图像 }
在主函数中添加点击跳转事件:
[code] public void colorMatrixClick(View view) { startActivity(new Intent(this, ColorMatrix.class)); }
最后,别忘了在 AndroidManifest.xml 注册。
运行程序,并点击 ColorMatrix 按钮,跳转到如下界面:
根据前面的颜色矩阵变换知识,在 4*5 矩阵输入不同的数值,并点击 Change 按钮即可对图片进行相应的操作。此外,点击 Reset 按钮可回到初始的效果。
3. 一些效果展示
有了之前的工具类 ImageHelper,就可以在里面添加各种处理效果了。这里添加三种处理效果为例,分别显示底片、怀旧和浮雕效果,代码如下:
[code] /** * 底片效果,循环处理所有的像素点 */ 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,把这几种效果显示出来,代码如下:
[code]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高仿雅虎天气(两)---代码结构分析
- Android 水平平均布局
- Android Studio使用注意事项
- Android25闹钟项目——ArrayAdapter动态添加数据,显示数据,删除数据SharedPreferences存储数据,读取数据
- 常用的android弹出对话框
- FFmpeg编译之后的Android平台移植---------阿冬
- Android属性动画完全解析(上),初识属性动画的基本用法
- Android异步任务AsyncTask
- Android25闹钟项目——刷新时间
- android:windowSoftInputMode属性详解
- Android25闹钟项目——tabhost的使用
- Android 实现用户列表信息的功能,然后选择删除幻灯片删除功能
- Android-onInterceptTouchEvent()和onTouchEvent()总结
- Android studio -SVN 使用笔记
- Android自定义Dialog
- Android的Window类
- Android ViewPager 如何判断当前页面是从前一页还是后一页滑动过来
- android EditText+ListView的组合(类似于AutoCompleteTextView)
- Android 程序框架设计
- Android 模块化编译的一些问题解决方案