GDI ColorMatrix的完全揭秘

2021-08-22 18:43:04 字數 4815 閱讀 9400

無論是用何種語言,只要使用過windows的gdi+的人對colormatrix都不陌生,我的blog文章中也多次提到過,並在《gdi+ for vcl基礎 -- 顏色調整矩陣colormatrix詳解

》一文中對其功能作了較為詳細的講解,雖然自認對colormatrix使用已經相當熟練,但對其原理也是知其然而不知其所以然。直到前幾天有位朋友就colormatrix實現影象去反功能不正常而問我(見我認為不大可能!眾所周知,用rgb主對角線-1矩陣實現影象求反是colormatrix重要功能之一,可是我試驗了多次,那張確實不能正常去反。我這人對自己自認熟悉的東西產生懷疑後,就有種不搞明白不罷休的衝動,為此,本人對colormatrix的原理作了透徹的解剖,不僅搞清楚了影象去反不正常的原因,還用**完整地實現了colormatrix功能!

本文主要揭秘gdi+ colormatrix調整原理,實現**另文介紹(可參見《delphi影象處理 -- 影象顏色矩陣調整》)。

gdi+ 提供用於儲存和操作影象的 image 和 bitmap 類。image 和 bitmap 物件將每個畫素的顏色都儲存為 32 位的數:紅色、綠色、藍色和 alpha 各佔 8 位。這四個分量的值都是 0 到 255,其中 0 表示沒有亮度,255 表示最大亮度。alpha 分量指定顏色的透明度:0 表示完全透明,255 表示完全不透明。

顏色向量採用 4 元組形式(紅色、綠色、藍色、alpha)。例如,顏色向量 (0, 255, 0, 255) 表示一種沒有紅色和藍色但綠色達到最大亮度的不透明顏色。

表示顏色的另一種慣例是用數字 1 表示亮度達到最大。使用這種慣例,上一段中描述的顏色將用 (0, 1, 0, 1) 表示。gdi+ 在進行顏色變換時使用以 1 表示最大亮度的慣例。

可通過用 4×4 矩陣乘以這些顏色向量將線性變換(旋轉和縮放等)應用到顏色向量中。但是,您不能使用 4×4 矩陣進行平移(非線性)。如果在每個顏色向量中再新增乙個虛擬的第 5 座標(例如,數字 1),則可使用 5×5 矩陣應用任何組合形式的線性變換和平移。由線性變換組成的後跟平移的變換稱為仿射變換。

說實話,不僅是剛開始接觸colormatrix的時候,就是現在的我看到這段文字,都覺得有點玄,前3段很好理解,特別是後面那段,更是使那些初識gdi+的人覺得colormatrix深奧無比:colormatrix是怎樣實現顏色的縮放、旋轉、剪下及平移的?靠這些功能能實現影象的哪些效果?或者說,某種效果能用colormatrix實現嗎?

下面,就讓我們一步步揭開colormatrix的神秘面紗,我相信,當你看完這篇文章後,一定會說:哦,原來如此,這麼簡單!其實,這就是所謂「江湖一張紙,戳破不值一分錢」!很多看起來技術含量很高的東西,被人千方百計保密,申請專利,搞的神秘無比,其實一旦公開,就那麼回事!

要揭秘colormatrix,就要解析它的縮放、旋轉、剪下及平移功能是怎麼實現的,為了方便敘述,我畫了個colormatrix矩陣圖貼在上面,在下面的表述中,大寫argb表示顏色各分量現有的值,而小寫argb表示運算後得到的新值。

1、顏色縮放:顏色縮放很簡單,就是按照給定的比例值,在影象畫素現有a、r、g、b各分量數值基礎上計算出新的分量值。這個比例值就是colormatrix主對角線除m55外的其它4個值。比如某畫素的rgba值現在分別為255、128、64和255,而主對角線m11 - m44的值分別為0.8、0.5、-1及0.5,那麼該畫素新的rgba值應該是:

r = r * m11 = 255 * 0.8 = 204;

g = g * m22 = 128 * 0.5 = 64;

b = b * m33 = 64 * -1 = 192;

a = a * m44 = 255 * 0.5 = 128;

是否很簡單?!可能有初學者說我上面b值計算錯了,64 * -1應該等於-64。沒錯,64用32位數表示為0xffffffc0,無符號位元組飽和取整,取最後8位0xc0,等於192。關於負值運算的問題,後面還要詳細講到的。

2、顏色剪下:一般說來,影象畫素r、g、b各分量按照與另一種顏色分量成比例的量來增加或減少顏色分量就是剪下。其實這種表述並不完全,畫素的a分量也是參與其中的!

以紅色分量r舉例,如果要按綠色分量g進行剪下,那麼m21就是剪下比例值,m21 * g就得到了g對r的剪下量。同理,m31 * b、m41 * a可分別得到b和a對r的剪下量,將這些剪下量加起來,就是r總的剪下量。用公式表示為:

r = g * m21 + b * m31 + a * m41;

g = r * m12 + b * m32 + a * m42;

b = r * m13 + g * m23 + a * m43;

a = r * m14 + g * m24 + b * m34;

3、顏色旋**顏色旋轉的描述比較複雜,就是在影象畫素中,用其中的2個分量按照一定的角度圍繞另外1個分量作運算的結果,就是顏色的旋轉。以紅色分量r和綠色分量g圍繞藍色分量g旋轉60度為例:

m11 = cos(60) = 0.5, m12 = sin(60) = 0.866,m21 = -sin(60) = -0.866, m22 = cos(60) = 0.5,那麼,r和g 所得到的旋轉量分別為:

r = r * m11(0.5) + g * m21(-0.866);

g = r * m12(0.866) + g * m22(0.5);

從上面的公式看,所謂的顏色旋轉量,其實就是旋轉的2個分量自身的縮放量加上與對方的剪下量而已!就運算角度看,同其它分量沒有任何關係。

4、顏色平移:上面的縮放、剪下和旋轉屬於顏色的線性變換(都是乘法運算的累積和),而平移是顏色的非線性變換,就是對顏色各分量做乙個加法而已:影象畫素各分量的平移量用所謂的虛擬位,即第5行的各個值來表示,各分量加上所在列的虛擬行的值就是顏色平移,其實質就是非線性地調整了該分量的亮度值。用公式表示各分量的平移量:

r = r + m51 * 255;

g = g + m52 * 255;

b = b + m53 * 255;

a = a + m54 * 255;

綜合以上顏色的縮放、旋轉、剪下及平移公式,對於顏色的每個分量r、g、b、a來說,運用colormatrix後所得到的實際值r、g、b、a,用公式表示為:

r = r * m11 + g * m21 + b * m31 + a * m41 + m51 * 255;

g = r * m12 + g * m22 + b * m32 + a * m42 + m52 * 255;

b = r * m13 + g * m23 + b * m33 + a * m43 + m53 * 255;

a = r * m14 + g * m24 + b * m34 + a * m44 + m54 * 255;

從技術層面上講,這個公式的含義就是顏色每個分量的新值,等於這個分量在colormatrix中對應列的前4行的值與r、g、a、b當前值的乘積之和加上第5行的值與常數255的乘積,而虛擬列(第5列)不起任何作用。

關於colormatrix功能的實現原理就這些,最後說說前面所提到的負值運算問題:colormatrix的負值運算無論是從原理上還是純技術實現上都很有些麻煩:

a)、在前面縮放舉例中,也就是只有主對角線有值的情況下,b的負值運算為b = b * m33 = 64 * -1 = 192,這是正確的,但是,如果加上乙個剪下量或者平移量,這個值就發生了一些變化。比如在縮放基礎上加上0.1的平移量,這似乎是很簡單的問題,直接用0.1 * 255加上去不就得了!可是,這個看是簡單的問題,卻有2個方案:是在縮放後的192基礎上加平移量0.1 * 255等於218?還是應該做連續運算:b = b * m33 = 64 * -1 + 255 * 0.1= -38(飽和處理後為0)呢?如果是前者,運算法則上似乎說不過去;如果是後者,感官上難以接受,本來b=192使顏色值「藍藍」的,本想讓它再「藍」一點,而加個平移(亮度)量,可即使這個平移量相當小,例如只平移0.001,卻讓b值變成了0,不僅沒有變的更「藍」,反而使顏色失去了藍色分量!可colormatrix偏偏用了使你感官難以接受的方案。其實也難怪設計者,只要順其自然去實現**,最後自然就成了這個結果。能夠造成這個結果的,不僅僅是平移量,只要矩陣中主對角線以外的任何乙個值不為0,包括不起作用的第5列,都會使主對角線中,具有負值所對應的rgba分量發生這種變化。具體請看本文中篇的**實現。

b)、前面也提到了有朋友用-1矩陣對影象求反不合要求的問題。通過上面的縮放公式計算一下,不難發現,用-1矩陣所得到的影象取反圖只是近似的!為什麼這麼說呢?所謂取反,就是可逆,如果用255減去r(gb)取反後,再用255去減就可還原;用255異和r(gb),不用說也是可逆的,而-1 * r(gb)卻是不完全可逆的:1 * -1飽和取整後為255,2 * -1為254 ......等等;反過來還原:結果是255 * -1 = 1、254 * -1 = 2......等等,從1 - 255都是可還原的,雖然這個還原於原本意義上的去反還原有1的誤差,但從人的感官上,這點誤差還是能接受的,只有「0」這個值是不可逆的,:0 * -1飽和取整還是0,無論是取反,還是還原,「0」依然是「0」,從而破壞了取反圖的協調,使人的感官完全無法接受,例如原來純紅色的畫素、取反後應該為青色,可是這裡卻變成了黑色,你能接受嗎?所以只要影象畫素rgb任何乙個分量為0,那麼這幅圖的取反效果就會出問題!有人可能說,我們都是這麼做的,除了極少數,大部分取反圖看上去還是正常的,難道這些正常圖裡面就沒有rgb分量為0的畫素?其實,一幅圖中間歇的分布幾個rgb分量為0的畫素,取反後,看上去是不那麼明顯的,所以我們認為這是成功的,只有那些「0」分量集中的部分,才嚴重破壞了圖的協調。

關於colormatrix原理上的揭秘就到此,由於我的文化理論水平有限,只能從技術角度來揭秘colormatrix的實現原理,說得不好,請原諒!

通過上面對colormatrix詳盡的技術解剖,我們推出了它的運算公式,有了這些,用程式**實現colormatrix功能,應該很容易了,本文中篇將介紹colormatrix功能的具體實現,相信各位高手應該比我更在行。

GDI ColorMatrix的完全揭秘

無論是用何種語言,只要使用過windows的gdi 的人對colormatrix都不陌生,我的blog文章中也多次提到過,並在 gdi for vcl基礎 顏色調整矩陣colormatrix詳解 一文中對其功能作了較為詳細的講解,雖然自認對colormatrix使用已經相當熟練,但對其原理也是知其然...

GDI ColorMatrix 彩色矩陣

colormatrix 彩色矩陣 類位於system.drawing.imaging命名空間 先看看下面的 colormatrix cm new colormatrix new float,new float,new float,new float,new float,new float 矩陣係數組...

container of 的的的原理

另外一篇,同樣精彩,揭開linux核心中container of的神秘面紗 華清遠見嵌入式學院講師。在linux 核心中有乙個大名鼎鼎的巨集container of 這個巨集是用來幹嘛的呢?我們先來看看它在核心中是怎樣定義的。呵呵,乍一看不知道是什麼東東。我們先來分析一下container of p...