內(nèi)容均來(lái)自
矩陣的概念
略,參考源博客
色彩矩陣
對(duì)于色彩的存儲(chǔ)弄屡,Bitmap類(lèi)使用一個(gè)32位的數(shù)值來(lái)保存屋彪。紅、綠允粤、藍(lán)及透明度各占8位,每一個(gè)色彩分量的取值范圍是0-255。透明度為0表示完全透明嚷硫,為255時(shí),色彩完全可見(jiàn)始鱼。
色彩的矩陣表示
四階矩形
一個(gè)色彩信息包含R仔掸、G、B医清、Alpha信息起暮,所以,必然要使用一個(gè)4階
色彩變換矩陣來(lái)修改色彩的每一個(gè)分量值(如果不想修改会烙,對(duì)應(yīng)的數(shù)值給0):
上圖中负懦,每個(gè)顏色占據(jù)一行,即占據(jù)一個(gè)維度柏腻,除了本分量值外纸厉,其他為0;
矩陣計(jì)算時(shí)葫盼,每行中的各個(gè)數(shù)字與另一矩陣中的一列中的各個(gè)數(shù)字分別相乘残腌,然后相加為新值;
注意:對(duì)于色彩變換矩陣贫导,這里的色彩順序是R抛猫、G、B孩灯、A而不是A闺金、R、G峰档、B败匹!
將色彩(0,255讥巡,0掀亩,255)更改為半透明時(shí),可以使用下面的的矩陣運(yùn)算來(lái)表示:
五階矩陣
使用四階矩陣完全可以改變圖片的RGBA值了欢顷,但考慮一種情況槽棍,如果我們只想在原有的R色上增加一些分量呢?
這時(shí),我們就得再多加一階來(lái)表示平移變換炼七。
一個(gè)既包含線性變換(乘法)
缆巧,又包含平移變換(加法)
的組合變換,稱(chēng)為仿射變換豌拙。
使用四階的色彩變換矩陣來(lái)修改色彩陕悬,只能夠?qū)ι实拿恳粋€(gè)分量值進(jìn)行乘(除)運(yùn)算,如果要對(duì)這些分量值進(jìn)行加減法的運(yùn)算(平移變換)按傅,只能通過(guò)五階矩陣來(lái)完成捉超。
比如:
1、紅色分量值更改為原來(lái)的2倍(線性)唯绍;
2狂秦、綠色分量增加100(平移);
則使用4階矩陣的乘法無(wú)法實(shí)現(xiàn)推捐,所以,應(yīng)該在四階色彩變換矩陣上增加一個(gè)“啞元坐標(biāo)”侧啼,來(lái)實(shí)現(xiàn)所列的矩陣運(yùn)算:
Android中的色彩矩陣
色彩變換矩陣的表示形式牛柒,是五階的,在默認(rèn)情況下痊乾,色彩變換矩陣的形式:
Android中的色彩矩陣是用ColorMatrix
類(lèi)來(lái)表示的皮壁,使用如下:
// 生成色彩矩陣
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.5, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
示例代碼1(單個(gè)顏色通道輸出)
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
setARGB(255, 200, 100, 100)
}
// 繪制原始位圖
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)
canvas.translate(width / 2.toFloat(), 0f)
// 生成色彩矩陣(把紅色和藍(lán)色都去掉,僅顯示綠色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)
示例代碼2(圖片多顏色單個(gè)綠色通道輸出)
private var bitmap: Bitmap
private var paint: Paint
init {
bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.chrome)
paint = Paint(Paint.ANTI_ALIAS_FLAG)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 繪制原始位圖
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
canvas.translate(0f, 260f)
// 生成色彩矩陣(把紅色和藍(lán)色都去掉蛾魄,僅顯示綠色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
}
色彩的幾種運(yùn)算方式
色彩的平移運(yùn)算
有了上面的鋪墊,這個(gè)理解起來(lái)就簡(jiǎn)單了湿滓;
色彩的平移運(yùn)算滴须,實(shí)際上就是色彩的加法運(yùn)算。就是在色彩變換矩陣的最后一行加上某個(gè)值叽奥;這樣可以增加特定色彩的飽和度扔水;
示例:為上圖增大綠色飽和度
// 飽和度綠色
val colorMatrix = ColorMatrix(floatArrayOf(
1f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 50f, // GREEN
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
注意:由于圖片是由一個(gè)個(gè)像素組成的,所以用每個(gè)像素所對(duì)應(yīng)的色彩數(shù)組朝氓,來(lái)乘轉(zhuǎn)換矩陣魔市,結(jié)果就是轉(zhuǎn)換后的當(dāng)前點(diǎn)的顏色值;所以赵哲,在應(yīng)用ColorMatrics后待德,圖片中每個(gè)像素的綠色值都增加了50;
色彩平移除了增加指定顏色飽和度以外枫夺,另一個(gè)應(yīng)用就是色彩反轉(zhuǎn):
// 色彩反相
val colorMatrix = ColorMatrix(floatArrayOf(
-1f, 0f, 0f, 0f, 255f, // RED
0f, -1f, 0f, 0f, 255f, // GREEN
0f, 0f, -1f, 0f, 255f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
色彩的縮放
色彩的縮放運(yùn)算其實(shí)就是色彩的乘法運(yùn)算将宪。在色彩矩陣對(duì)角線上的分別代表R、G、B涧偷、A的幾個(gè)值簸喂,將其分別乘以指定的值。這就是所謂的縮放變換燎潮。
可以針對(duì)某一個(gè)顏色值進(jìn)行放大縮小運(yùn)算喻鳄,但當(dāng)對(duì)R、G确封、B除呵、A同時(shí)進(jìn)行放大縮小時(shí),就是對(duì)亮度進(jìn)行調(diào)節(jié)爪喘!
// 色彩縮放颜曾,(全部為增加亮度)
val colorMatrix = ColorMatrix(floatArrayOf(
1.2f, 0f, 0f, 0f, 0f, // RED
0f, 1.2f, 0f, 0f, 0f, // GREEN
0f, 0f, 1.2f, 0f, 0f, // BLUE
0f, 0f, 0f, 1.2f, 0f)) // ALPHA
縮放變換的特殊應(yīng)用(通道輸出)
上面我們輸出過(guò)綠色通道;
由于在色彩變換矩陣中秉剑,對(duì)角線上的數(shù)的取值范圍是從0-1的泛豪,所以當(dāng)取0時(shí),這個(gè)色彩就完全不顯示侦鹏,所以當(dāng)我們R诡曙、G都取0,而獨(dú)有B取1時(shí)略水,就只顯示了藍(lán)色价卤,所形成的圖像也就是我們通常說(shuō)的藍(lán)色通道;
色彩的旋轉(zhuǎn)運(yùn)算
RGB色是如何旋轉(zhuǎn)的呢渊涝,首先用R慎璧、G、B三色建立立體坐標(biāo)系:
我們可以把一個(gè)色彩值看成三維空間里的一個(gè)點(diǎn)跨释,色彩值的三個(gè)分量可以看成該點(diǎn)的坐標(biāo)(三維坐標(biāo))胸私。我們先不考慮,在三個(gè)維度綜合情況下是怎么旋轉(zhuǎn)的鳖谈,我們先看看盖文,在某個(gè)軸做為Z軸,在另兩個(gè)軸形成的平面上旋轉(zhuǎn)的情況蚯姆,下圖分析了五续,在將藍(lán)色軸做為Z軸,僅在紅—綠平面上
旋轉(zhuǎn)a度的情況:
在圖中龄恋,我們可以看到疙驾,在旋轉(zhuǎn)后,原R在R軸的分量變?yōu)?原R*cosa(投影);
但原G分量在旋轉(zhuǎn)后郭毕,在R軸上也有了分量它碎,但分量落在了負(fù)軸上,所以我們要減去這部分分量,所以最終的結(jié)果是最終的R=原R*cosa-原G*sina;
下面就看下關(guān)于幾種旋轉(zhuǎn)計(jì)算及結(jié)果矩陣扳肛,(注意:這幾個(gè)圖只標(biāo)記了原X軸色彩分量的旋轉(zhuǎn)傻挂,沒(méi)有把Y軸色彩分量的旋轉(zhuǎn)標(biāo)記出來(lái))
繞藍(lán)色軸旋轉(zhuǎn)a°
對(duì)應(yīng)的色彩變換矩陣是:
繞紅色軸旋轉(zhuǎn)a度
對(duì)應(yīng)的色彩變換矩陣是:
繞綠色軸旋轉(zhuǎn)a度
對(duì)應(yīng)的色彩變換矩陣是:
當(dāng)圍繞紅色軸進(jìn)行色彩旋轉(zhuǎn)時(shí),由于當(dāng)前紅色軸的色彩是不變的挖息,而僅利用三角函數(shù)來(lái)動(dòng)態(tài)的變更綠色和藍(lán)色的顏色值金拒。這種改變就叫做色相調(diào)節(jié)!當(dāng)圍繞紅色軸旋轉(zhuǎn)時(shí)套腹,是對(duì)圖片就行紅色色相的調(diào)節(jié)绪抛;同理,當(dāng)圍繞藍(lán)色顏色軸旋轉(zhuǎn)時(shí)电禀,就是對(duì)圖片就行藍(lán)色色相調(diào)節(jié)幢码;當(dāng)然,當(dāng)圍繞綠色軸旋轉(zhuǎn)時(shí)尖飞,就是對(duì)圖片進(jìn)行綠色色相的調(diào)節(jié).
色彩的投射運(yùn)算
色彩矩陣運(yùn)算的公式:
紅色運(yùn)算給單獨(dú)拉了出來(lái)症副,紅色標(biāo)記的那幾個(gè)元素a12,a13,a14
,在運(yùn)算中,是利用G政基、B瓦糕、A的顏色值的分量
來(lái)增加紅色值的。
比如:
注意:最終結(jié)果的220=0.2*100+1*200,可見(jiàn)綠色分量在原有綠色分量的基礎(chǔ)上腋么,增加了紅色分量值的0.2倍;利用其它色彩分量的倍數(shù)來(lái)更改自己色彩分量的值亥揖,這種運(yùn)算就叫投射運(yùn)算
珊擂。
下圖陰影部分;對(duì)這些值進(jìn)行修改時(shí)费变,修改所使用的增加值來(lái)自于其它色彩分量的信息摧扇。
色彩投射的應(yīng)用之 - 黑白照片
// 黑白
colorMatrix = ColorMatrix(floatArrayOf(
0.213f, 0.715f, 0.072f, 0f, 0f, // RED
0.213f, 0.715f, 0.072f, 0f, 0f, // GREEN
0.213f, 0.715f, 0.072f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
彩色轉(zhuǎn)黑白原理:
- 只要把RGB三通道的色彩信息設(shè)置成一樣;即:R=G=B挚歧,那么圖像就變成了灰色扛稽;
- 為了保證圖像亮度不變,同一個(gè)通道中的R+G+B=1:如:0.213+0.715+0.072=1滑负;
三個(gè)數(shù)字的由來(lái):0.213, 0.715, 0.072在张;
按理說(shuō)應(yīng)該把RGB平分,都是0.3333333矮慕,可以試試0.33帮匾,原因還是看源博客吧;
投射運(yùn)算之色彩反色
利用色彩矩陣將兩個(gè)顏色反轉(zhuǎn)痴鳄,這種操作就叫做色彩反色 (如下:紅綠色反轉(zhuǎn))
colorMatrix = ColorMatrix(floatArrayOf(
0f, 1f, 0f, 0f, 0f, // RED, 由綠色替換
1f, 0f, 0f, 0f, 0f, // GREEN瘟斜,由紅色替換
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
投射運(yùn)算之顏色變舊
1/2f,1/2f,1/2f,0,0,
1/3f,1/3f,1/3f,0,0,
1/4f,1/4f,1/4f,0,0,
0,0,0,1,0
ColorMatrix函數(shù)
Android中ColorMatrix自帶了一些函數(shù)來(lái)幫我們完成一些調(diào)整飽和度、色彩旋轉(zhuǎn)等操作;
ColorMatrix的構(gòu)造函數(shù):
ColorMatrix() // new 一個(gè)新的
ColorMatrix(float[] src) // 指定創(chuàng)建
ColorMatrix(ColorMatrix src) // 以src為源copy 一個(gè)
setSaturation - 設(shè)置飽和度
通過(guò)色彩的平移運(yùn)算,可單獨(dú)增加R螺句、G虽惭、B的飽和度,但如果整體呢蛇尚,就需要用到 setSaturation
函數(shù)了芽唇;
// 整體增強(qiáng)顏色飽和度,即同時(shí)增強(qiáng)R,G,B的色彩飽和度
public void setSaturation(float sat)
參數(shù)float sat:表示把當(dāng)前色彩飽和度放大的倍數(shù)佣蓉。取值
- 0表示完全無(wú)色彩披摄,即灰度圖像(黑白圖像;
- 1時(shí)勇凭,表示色彩不變動(dòng)疚膊;
- 大于1時(shí),顯示色彩過(guò)度飽和虾标,飽和度放大多少倍寓盗;
setScale - 色彩縮放
// 分別對(duì)應(yīng)R,G,B,A顏色值的縮放倍數(shù)
public void setScale(float rScale, float gScale, float bScale,float aScale)
效果如下:
setRotate-色彩旋轉(zhuǎn)
參考源博客,上面介紹了色彩旋轉(zhuǎn)的原理璧函,涉及到正余弦函數(shù)傀蚌,同時(shí)Android封裝好了色彩旋轉(zhuǎn)的函數(shù):
/**
* 將旋轉(zhuǎn)圍繞某一個(gè)顏色軸旋轉(zhuǎn) degrees 度
* axis=0 圍繞紅色軸旋轉(zhuǎn)
* axis=1 圍繞綠色軸旋轉(zhuǎn)
* axis=2 圍繞藍(lán)色軸旋轉(zhuǎn)
*/
public void setRotate(int axis, float degrees);
示例代碼
seekBar {
id = android.R.id.text1
max = 360 // 整個(gè)旋轉(zhuǎn)度數(shù)的范圍是-180到180,360為一個(gè)周期
progress = 180
onSeekBarChangeListener {
onProgressChanged { _, progress, _ ->
view7.colorMatrix.setRotate(0,progress-180*.10f)
view7.postInvalidate()
}
}
}.lparams(width = ViewGroup.LayoutParams.MATCH_PARENT)
ColorMatrix相乘
涉及到矩陣相乘蘸吓;ColorMatrix矩陣相乘涉及到3個(gè)方法:
// matA*matB善炫,然后將結(jié)果做為當(dāng)前ColorMatrix的值(覆蓋當(dāng)前)
public void setConcat(ColorMatrix matA, ColorMatrix matB)
// 就是將當(dāng)前的矩陣乘以 prematrix
public void preConcat(ColorMatrix prematrix)
// postmatrix*當(dāng)前矩陣,上面為前乘库继,這里為后乘
public void postConcat(ColorMatrix postmatrix)
矩陣的前乘 與 后乘 的結(jié)果是不一樣的箩艺;
setConcat()
下面兩個(gè)顏色矩陣相乘:
因?yàn)榫仃囅喑说脑瓌t是:第一個(gè)矩陣的列數(shù)需等于第2個(gè)矩陣的行數(shù),明顯這里不是這樣的宪萄,那如何相乘呢艺谆?
Android提供了一個(gè)方案,讓這兩個(gè)矩陣相乘拜英,就是把第一個(gè)矩陣的最后一列單獨(dú)拿出來(lái)静汤,另外加到結(jié)果上;
參考源博客