2010-10-03 12 views
11

İlk defa bilgisayar grafik programlamasında oynuyorum. RGB (24 bit) görüntüleri indekslenmiş palet (8 bit) görüntülere (GIF gibi) dönüştürmek istiyorum. İlk düşüncem k-araçları kullanmaktır (k = 256 ile).Görüntü paleti azaltma

Belirli bir görüntü için en uygun paleti nasıl seçer? Bu benim için bir öğrenme deneyimidir, bu yüzden kaynak koduna genel bir bakış tercihini tercih ederim.

Düzenleme: Dithering şu anda konu dışı. Ben sadece "basit" renk dönüşümünü, psiko-görsel/algısal modelleri bir kenara atıyorum; renk boşluklar arasında hareket bana ilk etapta :)

+0

Bu basit bir işlem değildir. Bu tür bir dönüşüme girebilecek çok şey var (örneğin, dithering ve insan renk algısı). Sen sadece bir ağız dolusu ... tam bir üniversite dersi dolu, iyi bir soru için iyi bir soru için ... – JoshD

+0

+1 bahsettim ... cevabımı aşağıya bakın. 24 bit değerler ve standart "web güvenli" palet arasında dönüştürme ile başlamak isteyebilirsiniz. Bu, kendi paletinizi belirlemekten çok daha az karmaşık olacaktır (muhtemelen çok da eğlenceli değil). –

cevap

3

DÜZENLEME:

Şimdi
 
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 

size 4 * 4^3 = 256 olacaktır: 256 renk

en basit yöntemi gerekiyorsa o zaman histogram tabanlı bir yaklaşım öneririm paletini desteklemek için güncellendi renkler paleti. Piksele palet rengini atadığınızda, hangi yoğunluk bölgesini kullanmanız gerektiğini görmek için pikselin ortalama yoğunluğunu hesaplamanız yeterlidir. Bundan sonra, yoğunluk bölgesinin bu 64 renk noktasından birini piksel değerine eşleyin.

İyi şanslar.

+0

Yani, her (RGB) kanal için 4 yoğunluklu kovaya sahibim (0-63, 64-127, 128-191, 192-255). Bunların her biri ayrıca 4 alt aralıklı kepçelere ayrılmıştır (ör. 0-63 arası aralık için 0-15, 16-31, 32-47, 48-63); Kanal başına toplam 16 kova verilmesi. Her böyle (alt) kova, ortalama değeri (R [0-15] alt grubu için 12) şeklinde temsil edilecektir. Şimdi toplam 16 Kırmızı (ve Yeşil ile Mavi için aynı), toplam 16 * 16 * 16 = 4096 renk var mı ?! Neyi yanlış yaptım ?! – Trevor

+0

Çünkü farklı yoğunluk aralığı kovalarından renkleri karıştırdınız. Algoritma fikri, hangi yoğunluk kepçesinin kullanılacağını bulmaktır - örneğin piksel yoğunluğunu hesaplamak Ip = (R + G + B)/3. Daha sonra bu Ip ile yoğunluk kovasını seçin ve üç R/G/B kanalı için SAME yoğunluk kovasını kullanın. (Bazı değerler yoğunluk aralığının dışındaysa, geçerli aralıkta en soldaki veya en sağdaki alt-kova kullanın). Yani, devam - farklı kovalardan alt kovaları karıştırmayın ve her şey iyi olacak. Ayrıca alt-kova için ortalama değer kullanmayın - en sık kullanılan değeri kullanın, aksi halde bu yöntem işe yaramaz. –

+0

Ya da 8 * 8 * 4 yaklaşımını deneyebilirsiniz (hiç bir alt-kova olmadan), çünkü insan gözü kırmızıya karşı daha yeşil, maviye göre daha yeşildir. –

4

İnsanların sağladığı referans bağlantıları iyi ve bu soruna birçok çözüm var, ama bu problem üzerinde yakın zamanda çalıştığım için (başkalarının nasıl çözdüğüne dair tam bir cehaletle), yaklaşımımı sunarım Düz İngilizce:

Öncelikle (insan algılanan) rengin 3 boyutlu olduğunu anlayın. Bu temel olarak insan gözünün 3 farklı reseptörü olduğu için: kırmızı, yeşil ve mavi. Aynı şekilde monitörünüzün kırmızı, yeşil ve mavi piksel elemanları vardır. Ton, doygunluk, parlaklık (HSL) gibi diğer gösterimler kullanılabilir, ancak temel olarak tüm sunumlar 3 boyutludur.

Bu, RGB renk uzayının kırmızı, yeşil ve mavi eksenli bir küp olduğu anlamına gelir. 24 bitlik bir kaynak görüntüden, bu küpün her bir eksende 256 ayrık seviyesi vardır. Görüntüyü 8-bit'e indirgemeye yönelik naif bir yaklaşım, sadece eksen başına seviyeyi azaltmaktır. Örneğin, kırmızı ve yeşil için 8 seviyeli 8x8x4 küp paleti, kırmızı ve yeşil değerlerin yüksek 3 bitini ve mavi değerin yüksek 2 bitini alarak 4 seviyeli mavi için kolayca yaratılır. Bu uygulanması kolaydır, ancak bazı dezavantajları vardır. Elde edilen 256 renk paletinde, pek çok giriş kullanılmayacak. Görüntünün çok ince renk değişimlerini kullanarak ayrıntıları varsa, renkler aynı palet girişine eklendikçe bu kaymalar kaybolacaktır.

Uyarlamalı bir palet yaklaşımı, görüntüdeki yalnızca ortalama/ortak renkleri değil, aynı zamanda hangi renk alanı alanlarının en büyük varyansa sahip olduğunu da hesaba katmalıdır.Yani, açık yeşilin binlerce ince tonuna sahip bir görüntü, tam olarak aynı açık yeşil tonda binlerce piksele sahip bir görüntüden farklı bir palet gerektirir; çünkü bu, bu renk için ideal olarak tek bir palet girdisini kullanır.

Bu amaçla, her biri aynı sayıda farklı değer içeren 256 paket içeren bir yaklaşımla karşılaştım. Eğer orijinal görüntü 256000 farklı 24 bit renk içeriyorsa, bu algoritma her biri 1000 orijinal değer içeren 256 kovaya neden olur. Bu, mevcut olan farklı değerlerin medyanını (ortalama değil) kullanarak renk uzayının ikili uzaysal bölümlenmesi ile gerçekleştirilir.

Türkçe, bu, tüm renk küpünü ilk olarak, medyan kırmızı değerden daha az olan ve yarının ortanca kırmızı değerinden daha fazla olan piksellerin yarısına bölünmesi anlamına gelir. Ardından, oluşan her bir yarıyı yeşil değere, sonra maviye ve benzeri şekilde bölün. Her bölme, piksellerin alt veya üst yarısını belirtmek için tek bir bit gerektirir. 8 bölmeden sonra, varyans etkin bir şekilde renk uzayında 256 eşit derecede önemli kümeye ayrılmıştır. psuedo kodunda

:

// 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 
    } 
} 

Artık renk küpünde mekansal kümelenmiş 256 yaprak düğümleri, orijinal görüntüden farklı renklerin aynı sayıda içeren her vardır. Her bir düğüme tek bir renk atamak için, renk sayılarını kullanarak ağırlıklı ortalamayı bulun. Ağırlık, algısal renk eşleştirmesini geliştiren bir optimizasyondur, ancak bu önemli değildir. Her bir renk kanalını bağımsız olarak ortalama yaptığınızdan emin olun. Sonuçlar mükemmel. Gözdeki mavi reseptörlerin kırmızı ve yeşilden daha az hassas değişikliklere daha az duyarlı olduğu için, mavinin kırmızı ve yeşilden daha az bir kez ayrıldığına dikkat edilmelidir.

Başka optimizasyonlar da mümkündür. HSL'yi kullanarak daha yüksek niceleme değerini mavi yerine parlaklık boyutuna koyabilirsiniz. Ayrıca, yukarıdaki algoritma genel dinamik aralığını biraz azaltacaktır (sonuçta renk değerlerini ortalama olarak aldığından), sonuçta ortaya çıkan paleti dinamik olarak genişletmek başka bir olasılıktır.