OPhone中的圖片特效處理
在OPhone編程中有時候需要對圖片做特殊的處理,比如將圖片做出黑白的,或者老照片的效果,有時候還要對圖片進行變換,以拉伸,扭曲等等。這些效果在OPhone中有很好的支持,通過顏色矩陣(ColorMatrix)和坐標變換矩陣(Matrix)可以完美的做出上面的所說的效果,下面將分別介紹這兩個矩陣的用法和相關的函數。
顏色矩陣
OPhone中可以通過顏色矩陣(ColorMatrix類)方面的操作顏色,顏色矩陣是一個5x4 的矩陣(如圖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矩陣是圖片中包含的ARGB信息,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;
顏色矩陣并不是看上去那么深奧,其實需要使用的參數很少,而且很有規律第一行決定紅色第二行決定綠色,第三行決定藍色,第四行決定了透明度,第五列是顏色的偏移量。下面是一個實際中使用的顏色矩陣。

如果把這個矩陣作用于各顏色分量的話,R=A*C,計算后會發現,各個顏色分量實際上沒有任何的改變(R'=R G'=G B'=B A'=A)。

圖1.5所示矩陣計算后會發現紅色分量增加100,綠色分量增加100,這樣的效果就是圖片偏黃,因為紅色和綠色混合后得到黃色,黃色增加了100,圖片當然就偏黃了。

改變各顏色分量不僅可以通過修改第5列的顏色偏移量也可如上面矩陣所示將對應的顏色值乘以一個倍數,直接放大。上圖1.6是將綠色分量乘以2變為原來的2倍。相信讀者至此已經明白了如何通過顏色矩陣來改變各顏色分量。下面編寫一段代碼來,通過調整顏色矩陣來獲得不同的顏色效果,JavaCode如下:
CMatrix類:
public class CMatrix extends Activity {
private Button change;
private EditText [] et=new EditText[20];
private float []carray=new float[20];
private MyImage sv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
change=(Button)findViewById(R.id.set);
sv=(MyImage)findViewById(R.id.MyImage);
for(int i=0;i<20;i++){
et[i]=(EditText)findViewById(R.id.indexa+i);
carray[i]=Float.valueOf(et[i].getText().toString());
}
change.setOnClickListener(l);
}
private Button.OnClickListener l=new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
getValues();
sv.setValues(carray);
sv.invalidate();
}
};
public void getValues(){
for(int i=0;i<20;i++){
carray[i]=Float.valueOf(et[i].getText().toString());
}
}
}
MyImage類繼承自View類:
public class MyImage extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBitmap;
private float [] array=new float[20];
private float mAngle;
public MyImage(Context context,AttributeSet attrs) {
super(context,attrs);
mBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.test);
invalidate();
}
public void setValues(float [] a){
for(int i=0;i<20;i++){
array[i]=a[i];
}
}
@Override protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
paint.setColorFilter(null);
canvas.drawBitmap(mBitmap, 0, 0, paint);
ColorMatrix cm = new ColorMatrix();
//設置顏色矩陣
cm.set(array);
//顏色濾鏡,將顏色矩陣應用于圖片
paint.setColorFilter(new ColorMatrixColorFilter(cm));
//繪圖
canvas.drawBitmap(mBitmap, 0, 0, paint);
Log.i("CMatrix", "--------->onDraw");
}
}
public class CMatrix extends Activity {
private Button change;
private EditText [] et=new EditText[20];
private float []carray=new float[20];
private MyImage sv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
change=(Button)findViewById(R.id.set);
sv=(MyImage)findViewById(R.id.MyImage);
for(int i=0;i<20;i++){
et[i]=(EditText)findViewById(R.id.indexa+i);
carray[i]=Float.valueOf(et[i].getText().toString());
}
change.setOnClickListener(l);
}
private Button.OnClickListener l=new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
getValues();
sv.setValues(carray);
sv.invalidate();
}
};
public void getValues(){
for(int i=0;i<20;i++){
carray[i]=Float.valueOf(et[i].getText().toString());
}
}
}
MyImage類繼承自View類:
public class MyImage extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBitmap;
private float [] array=new float[20];
private float mAngle;
public MyImage(Context context,AttributeSet attrs) {
super(context,attrs);
mBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.test);
invalidate();
}
public void setValues(float [] a){
for(int i=0;i<20;i++){
array[i]=a[i];
}
}
@Override protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
paint.setColorFilter(null);
canvas.drawBitmap(mBitmap, 0, 0, paint);
ColorMatrix cm = new ColorMatrix();
//設置顏色矩陣
cm.set(array);
//顏色濾鏡,將顏色矩陣應用于圖片
paint.setColorFilter(new ColorMatrixColorFilter(cm));
//繪圖
canvas.drawBitmap(mBitmap, 0, 0, paint);
Log.i("CMatrix", "--------->onDraw");
}
}
CMatrix類主要負責接收顏色矩陣的設置和重繪,沒有要說的。MyImage類中進行繪圖工作,首先設置顏色矩陣cm.set(..)從一維數組中讀取數據20個數據給顏色矩陣賦值,paint.setColorFilter(..)設置顏色濾鏡,然后繪圖,效果就出來了(這個過程和PS差不多)如下:



看到這里,相信大家對顏色矩陣的作用已經有了一個直觀的感受,現在也可以嘗試做一個照片特效的軟件。但是各種效果并不能讓用戶手動調節顏色矩陣,這里需要計算公式,由于本人并不是做圖形軟件的也不能提供,可以參考這個鏈接:
坐標變換矩陣
坐標變換矩陣是一個3*3的矩陣如圖2.1,用來對圖形進行坐標變化,將原來的坐標點轉移到新的坐標點,因為一個圖片是有點陣和每一點上的顏色信息組成的,所以對坐標的變換,就是對每一點進行搬移形成新的圖片。具體的說圖形的放大縮小,移動,旋轉,透視,扭曲這些效果都可以用此矩陣來完成。

這個矩陣的作用是對坐標x,y進行變換計算結果如下:
x'=a*x+b*y+c
y'=d*x+e*y+f
通常情況下g=h=0,這樣使1=0*x+0*y+1恒成立。和顏色矩陣一樣,坐標變換矩陣真正使用的參數很少也很有規律。

上圖就是一個坐標變換矩陣的簡單例子,計算后發現x'=x+50,y'=y+50.可見圖片的每一點都在x和y方向上平移到了(50,50)點處,這種效果就是平移效果,將圖片轉移到了(50,50)處。

計算上面得矩陣x'=2*x,y‘=2*y.經過顏色矩陣和上面轉移效果學習,相信讀者可以明白這個矩陣的作用了,這個矩陣對圖片進行了放大,具體的說是放大了二倍。
下面將介紹幾種常用的變換矩陣:
1. 旋轉

2. 縮放

變換后長寬分別放大x'=scale*x;y'=scale*y.
3. 切變

4. 反射

( , )單位向量
5. 正投影

( , )單位向量
上面的各種效果也可以疊加在一起,既矩陣的組合變換,可以用矩陣乘法實現之,如:R=B(A*C)=(B*A)C,注意一點就是B*A和A*B一般是不等的。下面將編一個小程序,通過控制坐標變換矩陣來達到控制圖形的目的,JavaCode如下:
CooMatrix類:
public class CooMatrix extends Activity {
private Button change;
private EditText [] et=new EditText[9];
private float []carray=new float[9];
private MyImage sv;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
change=(Button)findViewById(R.id.set);
sv=(MyImage)findViewById(R.id.MyImage);
for(int i=0;i<9;i++){
et[i]=(EditText)findViewById(R.id.indexa+i);
carray[i]=Float.valueOf(et[i].getText().toString());
}
change.setOnClickListener(l);
}
private Button.OnClickListener l=new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
getValues();
sv.setValues(carray);
sv.invalidate();
}
};
public void getValues(){
for(int i=0;i<9;i++){
carray[i]=Float.valueOf(et[i].getText().toString());
}
}
}
MyImage類繼承自View類:
public class MyImage extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBitmap;
private float [] array=new float[9];
public MyImage(Context context,AttributeSet attrs) {
super(context,attrs);
mBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_launcher_android);
invalidate();
}
public void setValues(float [] a){
for(int i=0;i<9;i++){
array[i]=a[i];
}
}
@Override protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
canvas.drawBitmap(mBitmap, 0, 0, paint);
//new 一個坐標變換矩陣
Matrix cm = new Matrix();
//為坐標變換矩陣設置響應的值
cm.setValues(array);
//按照坐標變換矩陣的描述繪圖
canvas.drawBitmap(mBitmap, cm, paint);
Log.i("CMatrix", "--------->onDraw");
}
}
上面的代碼中類CooMatrix用于接收用戶輸入的坐標變換矩陣參數,類MyImage接收參數,通過setValues()設置矩陣參數,然后Canvas調用drawBitmap繪圖。效果如下:


上面給出了用坐標變換矩陣做出的各種效果,用坐標變換矩陣可以方面的調節圖形的各種效果,但是我們看看Matrix類就可以發現,實際上,matrix類本身已經提供了許多類似的方法,我們只要調用,就可以了。
- setScale(float sx, float sy, float px, float py) 放大
- setSkew(float kx, float ky, float px, float py) 斜切
- setTranslate(float dx, float dy) 平移
- setRotate(float degrees, float px, float py) 旋轉
上面的函數提供了基本的變換平移,放大,旋轉,斜切。為了做出更復雜的變換,同時不必親手去改動坐標變換矩陣,Matrix類提供了許多Map方法,將原圖形映射到目標點構成新的圖形,下面簡述setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) 的用法,希望起到舉一反三的作用。參數src和dst是分別存儲了原圖像的點和和指定的目標點的一維數組,數組中存儲的坐標格式如下:
[x0, y0, x1, y1, x2,y2,...]
這個個函數將src中的坐標映射到dst中的坐標,實現圖像的變換。具體的例子可以參考APIDemos里的PolyToPoly,我在這里就不再貼代碼了,只講一下函數是怎么變換圖片的。下面是效果:

圖中寫1的是原圖,寫有2,3,4的是變換后的圖形。現在分析2是怎么變換來的,變換的原坐標點和目的坐標點如下:
src=new float[] { 32, 32, 64, 32 }
dst=new float[] { 32, 32, 64, 48 }

從上圖標示出的坐標看出原圖的(32,32)映射到原圖的(32,32),(64,32)映射到原圖(64,48)這樣的效果是圖像放大了而且發生了旋轉。這樣的過程相當于(32,32)點不動,然后拉住圖形(64,32)點并拉到(64,48)點處,這樣圖形必然會被拉伸放大并且發生旋轉。最后用一個平移將圖形移動到右邊現在的位置。希望能夠好好理解這一過程,下面的3,4圖是同樣的道理。Matrix還有許多類似的Map方法可以做出許多效果。詳細可以參考Matrix類下的方法。
浙公網安備 33010602011771號