2010-10-03 60 views
11

我正在玩電腦圖形編程的第一次。我想將RGB(24位)圖像轉換爲索引調色板(8位)圖像(如GIF)。我最初的想法是使用k-means(k = 256)。調色板縮減

如何去挑選給定圖像的最佳調色板?這對我來說是一種學習體驗,所以我更願意對源代碼提供概覽型答案。

編輯:抖動目前是脫離主題。我只提到「簡單」的顏色轉換,拋開心理視覺/感知模型;色彩空間也是目前題外話,雖然色彩空間之間運動是什麼讓我擺在首位:)

+0

這不是一件簡單的事情。有很多可以進入這種類型的轉換(例如抖動和人類色彩感知)。你只是問了一口滿滿的......整個大學課程,我打賭:) – JoshD 2010-10-03 08:39:22

+0

+1一個很好的問題......看到我的答案在下面。您可能希望從24位值和標準的「網頁安全」調色板之間進行轉換。這比確定自己的調色板要複雜得多(儘管可能不那麼有趣)。 – 2010-10-03 09:00:44

回答

3

編輯: 更新,可支持256個色

如果需要簡單的方法,那麼我建議根據統計圖方式調色板:

 
Calculate histograms of R/G/B channels 
Define 4 intensity ranges 
For each channel in intensity range 
    Split histogram into 4 equal parts 
    For each histogram part 
    Extract most frequent value of that part 

現在你將有4 * 4^3 = 256調色板。將像素分配給調色板顏色時,只需計算像素的平均強度即可查看必須使用的強度區域。之後,只需將這64個強度區域中的一個映射到像素值即可。

祝你好運。

+0

因此,對於每個(RGB)通道,我有4個強度範圍桶(0-63,64-127,128-191,192-255)。其中每一個進一步分成4個子範圍桶(例如[0-63]範圍內的0-15,16-31,32-47,48-63);每個通道共提供16個桶。每個這樣的(子)桶將以其平均值的形式表示(對於R [0-15]子桶來說是12)。 我現在總共有16個紅色(綠色和藍色相同),共16 * 16 * 16 = 4096種顏色?! 我錯了什麼? – Trevor 2010-10-04 12:26:46

+0

這是因爲您混合了來自不同強度範圍桶的顏色。算法的想法是找出要使用哪個強度桶 - 例如計算像素強度Ip =(R + G + B)/ 3。然後通過該Ip選擇強度桶,併爲所有三個R/G/B通道使用SAME強度桶。 (如果某個值超出強度範圍,則使用當前範圍內最左側或最右側的子桶)。所以,簡歷 - 不要混合來自不同桶的子桶,一切都會好的。也不要將平均值用於子桶 - 使用最常用的值,否則此方法將不起作用。 – 2010-10-04 13:51:40

+0

或者你也可以嘗試8 * 8 * 4的方法(根本沒有子桶),因爲人眼對紅色,綠色比對藍色更敏感。 – 2010-10-05 06:41:10

4

人們提供的參考鏈接是很好的,這個問題有幾種解決方案,但由於我最近一直在處理這個問題(完全不知道別人如何解決它),我提供了我的方法用簡單的英文:

首先,認識到(人類感知的)顏色是三維的。這基本上是因爲人眼有3種不同的受體:紅色,綠色和藍色。同樣,您的顯示器也具有紅色,綠色和藍色像素元素。其他表示,如色調,飽和度,亮度(HSL)可以使用,但基本上所有的表示都是三維的。

這意味着RGB色彩空間是一個具有紅色,綠色和藍色軸的立方體。從24位源圖像中,該立方體在每個軸上有256個離散水平。將圖像縮小爲8位的簡單方法是簡單地降低每個軸的級別。例如,通過獲取紅色和綠色值的高3位以及藍色值的高2位,可以輕鬆創建一個8x8x4立方體調色板,其中8級爲紅色和綠色,4級爲藍色。這很容易實現,但有幾個缺點。在最終的256色調色板中,許多條目根本不會被使用。如果圖像具有使用非常細微的顏色偏移的細節,則這些偏移將隨着顏色卡入相同的調色板條目而消失。

自適應調色板方法不僅要考慮圖像中的平均/常見顏色,還要考慮顏色空間中哪些區域具有最大變化。也就是說,具有數千種淡綠色的圖像需要與具有完全相同的淺綠色的數千個像素的圖像不同的調色板,因爲後者將理想地使用該顏色的單個調色板條目。

爲此,我採取了一種方法,其結果是每個包含256個完全相同數量的不同值的桶。因此,如果原始圖像包含256000個不同的24位顏色,則此算法會生成256個存儲桶,每個存儲桶包含1000個原始值。這是通過使用存在的不同值的中值(而不是平均值)對顏色空間進行二元空間分割來完成的。

在英文中,這意味着我們首先將整個顏色立方體分成小於中值紅色值的一半像素和大於中值紅色值的一半。然後,將每個結果的一半除以綠色值,然後再除以藍色,依此類推。每個分割都需要一個位來指示像素的較低或較高的一半。經過8次分割後,方差已被有效地分割成色彩空間中的256個同樣重要的簇。

在僞碼:

// count distinct 24-bit colors from the source image 
// to minimize resources, an array of arrays is used 
paletteRoot = {colors: [ [color0,count],[color1,count], ...]} // root node has all values 
for (i=0; i<8; i++) { 
    colorPlane = i%3 // red,green,blue,red,green,blue,red,green 
    nodes = leafNodes(paletteRoot) // on first pass, this is just the root itself 
    for (node in nodes) { 
    node.colors.sort(colorPlane) // sort by red, green, or blue 
    node.lo = { colors: node.colors[0..node.colors.length/2] } 
    node.hi = { colors: node.colors[node.colors.length/2..node.colors.length] } 
    delete node.colors // free up space! otherwise will explode memory 
    node.splitColor = node.hi.colors[0] // remember the median color used to partition 
    node.colorPlane = colorPlane // remember which color this node split on 
    } 
} 

現在有256個葉節點,每個節點包含從原始圖像相同數量的不同的顏色,在顏色立方體空間聚類。要爲每個節點分配一種顏色,請使用顏色計數查找加權平均值。加權是提高感知色彩匹配的優化,但並不重要。確保平均每個顏色通道獨立。結果非常好。請注意,故意將藍色分成一次少於紅色和綠色,因爲眼睛中的藍色感受器對微妙變化的敏感度低於紅色和綠色。

還有其他優化可能。通過使用HSL,您可以將更高量化的亮度維數而不是藍色。此外,上述算法會稍微減少整體動態範圍(因爲它最終會平均顏色值),因此動態擴展所得調色板是另一種可能。