Renk Dönüşümleri ve Dönüşüm Matrisi

Renkler görüntüleri oluşturan temel kavramlardır. Işığın bir cisme çarpıp belirli dalga boylarının soğrulması ve belirli dalga boylarını yansıtması ile gözümüzde renk algısı oluşur. Bu yansıma ile gelen ışınların dalga boyu 380nm-780nm arasında ise insan gözünde dalga boyu ile ilintili bir renk oluşur. Renk dönüşümleri ise günümüzde özellikle instagram benzeri uygulamalar ile göze hoş gelen fotoğraflar yaratmak için kullanılsa da,  pek çok görüntü işleme uygulamasında bir ön işlem olarak sıklıkla kullanılan dönüşümlerdir. Bu yazımda öncelikle bir renk dönüşüm matrisi tanımlayarak, bunu cbmp kütüphanemiz içerisinde bir fonksiyon olarak kodlayacağız.


Bu işlem sonrasında istenilen renk dönüşümünü elde etmek için dönüşüm matrisimizin değerlerini değiştirmemiz yetecek.
Temel olarak bir rengin 3 kanaldan oluştuğunu biliyoruz. Amacımız bu 3 kanalı kullanarak yeni bir renk elde etmek. Bunu yapmak için basit olarak aşağıdaki denklemi yazabiliriz.

Y = axR + bxG + cxB + d

Bu denklemde sırasıyla a, b, c yeni oluşturulan renkte kırmızı, yeşil ve mavi kanalın katkısını, d ise ifadeyi genelleştirmek için kullanılan ve bu katkılara ek olarak sabit bir değer ile toplama işlemini modellemek için kullanıldı. Burada Y dönüşüm sonucu oluşan yeni resimdeki bir renk kanalını göstermektedir. Renkli her resimde 3 kanal (RGB) bulunduğundan, yukarıda verilen eşitliği bir 3x4 bir matris yardımıyla şu şekilde ifade etmek mümkündür.

Dönüşüm Matrisi
Yukarıda görülen dönüşüm matrisi bir resmin RGB kanalları kullanılarak, lineer işlemler sonucu elde edilebilecek tüm sonuçları alabilmemizi sağlayacak genel bir yapıdadır. Bu işlemin c kodları aşağıda verilmiştir. Verilen kodlar içerisinde tanımlanan 256 uzunluklu tr vektörleri işlemleri hızlandırmak için hazırlanmıştır. Byte türündeki bir resimde pixel değerleri 0-255 arasında olacağından, matris çarpımına başlamadan 0-255 aralığındaki her değer dönüşüm matrisinin elemanları ile çarpılmış ve ilgili isimde bir vektörde saklanmıştır. Dönüşüm sırasında ise P pixel değerinin yeni değeri, hiç çarpma işlemi yapılmayarak, doğrudan ilgili vektörün P. indisindeki değer kullanılarak hesaplanmıştır.

BMP   resim_cev(BMP kaynak,double tr[3][4]) {
      
      BMP im=yenim_bmp(kaynak.bminfo.width,kaynak.bminfo.height);
      int i=0;
      
      double tr00[256]; //1. kanalın yeni 1. kanala etkisi
      double tr01[256]; //2. kanalın yeni 1. kanala etkisi
      double tr02[256]; //3. kanalın yeni 1. kanala etkisi 
      double tr03 = tr[0][3]; // 1. kanala eklenecek sabit değer
      
      double tr10[256]; //1. kanalın yeni 2. kanala etkisi
      double tr11[256]; //2. kanalın yeni 2. kanala etkisi
      double tr12[256]; //3. kanalın yeni 2. kanala etkisi 
      double tr13 = tr[1][3]; // 2. kanala eklenecek sabit değer
      
      double tr20[256]; //1. kanalın yeni 3. kanala etkisi
      double tr21[256]; //2. kanalın yeni 3. kanala etkisi
      double tr22[256]; //3. kanalın yeni 3. kanala etkisi 
      double tr23 = tr[2][3]; // 3. kanala eklenecek sabit değer
      
      //Hız kazanmak için 0-255 çarpmalarını önceden yap
      for(i=0;i < 256;i++) {
                         tr00[i] = i*tr[0][0];
                         tr01[i] = i*tr[0][1];
                         tr02[i] = i*tr[0][2];
                         tr10[i] = i*tr[1][0];
                         tr11[i] = i*tr[1][1];
                         tr12[i] = i*tr[1][2];
                         tr20[i] = i*tr[2][0];
                         tr21[i] = i*tr[2][1];
                         tr22[i] = i*tr[2][2];
       }
                         
       for(int j=0;j < kaynak.bminfo.height;j++) { 
            for(int i=0;i < kaynak.bminfo.width;i++) {
                            
               im.pixels[i][j].red   =  gbyte_yap(tr00[kaynak.pixels[i][j].red]+tr01[kaynak.pixels[i][j].green]+tr02[kaynak.pixels[i][j].blue] + tr03);
               im.pixels[i][j].green =  gbyte_yap(tr10[kaynak.pixels[i][j].red]+tr11[kaynak.pixels[i][j].green]+tr12[kaynak.pixels[i][j].blue] + tr13);
               im.pixels[i][j].blue  =  gbyte_yap(tr20[kaynak.pixels[i][j].red]+tr21[kaynak.pixels[i][j].green]+tr22[kaynak.pixels[i][j].blue] + tr23);
            }
      }

return im;   
}

Dönüşüm matrisimizi fonksiyon olarak yazdıktan sonra artık denemelerimize başlayabiliriz. Görüntü işlemede sıklıkla kullanılan, basit dönüşümlerle testlerimize başlayabiliriz. İşlemlerin tamamında aşağıdaki kod parçası, dönüşüm matrisi değiştirilerek kullanılmıştır.

BMP I   = resim_oku("1.bmp");

double rgb2gra[3][4] = {
           {0.299, 0.587, 0.114, 0},
           {0.299, 0.587, 0.114, 0},
           {0.299, 0.587, 0.114, 0}
           };

BMP T = resim_cev(I,rgb2gra);
       
resim_yaz(T,"gray.bmp");

Kanal Ayırma

Bu işlemde resme ait RGB kanalları tek tek ayrılarak, her bir kanalın özgün katkısı incelenmiştir. Bu işlem için kolayca anlaşılabileceği üzere dönüşüm matrisimiz aşağıdaki şekilde oluşturulmalıdır:




Yukarıda verilen sonuçlar, renkli bir resmi oluşturan 3 ayrı kanalın ayrı ayrı görünümleridir. Burada dikkat edilecek bir nokta, mavi kanalda detaylar çok az belirginken, yeşil kanalda detayların çoğunun görünür olduğudur. Bunun en önemli sebebi; gözümüzün mavi ışığın var olduğu dalga boyuna daha az hassas olmasıdır. Yani aynı gri seviye görüntüyü mavi ve yeşil olarak renklendirir-sek, yeşil kodlanan görüntü gözümüze daha fazla bilgi içeriyor şeklinde görünecektir. Gece görüş sistemlerinde yeşil renk görüntü verilmesinin temel sebebi de budur.

Kanal Çevirme

Kanal çevirme de görüntü işlemede ender kullanılan basit dönüşümlerden biridir. Mantık herhangi bir kanalın değerini 255 e tümleyen-ine çevirmektir. Bu işlemi yapmak için gerekli dönüşüm matrisi, dönüşüm matrisimizde-ki sabitimizden faydalanarak, şu şekilde oluşturulur:



Gri Seviye Dönüşümü

Gri RGB kanallarından üçünün de aynı değere sahip olduğunda oluşan bir renktir (Bazı söylemlere göre beyaz ve siyah da grinin bir tonu olduğundan, gri de renk değil tondur ?). Yukarıda detaylı şekilde verdiğim mavi renge olan duyarsızlığımızdan sonra, gri seviye dönüşüm matrisimizi rahatlıkla verebiliriz. Burada matrisin ağırlıklarının insan gözünün ilgili bileşene olan duyarlılığı ile doğru olduğunu unutmayalım. Söylediğim gibi hangi katsayıları kullanırsak kullanalım R'G'B' değerlerini aynı yaptıktan sonra oluşacak görüntü gri seviyedir.


Sepya Dönüşümü

Yazımı son olarak günümüzde sıklıkla kullanılan sepya dönüşümü ile bitiriyorum. Sepya kırmızı ve yeşilin çok hafif tonlarından oluşmuş, gri seviye benzeri hoş bir tonlamadır. Microsoft tarafından sunulan standart sepya dönüşüm ağırlıkları, örnek çevrim ile beraber aşağıda verilmiştir.


2 yorum:

  1. iyi akşamlar, acaba gri dönüşümü kaynak kodu elinizde var mı? varsa acilen lazım yardımcı olursanız sevinirim.

    YanıtlayınSil
  2. Merhaba, rgb2gray koduna https://github.com/cescript/imlab_library/blob/master/source_code/imcore/sources/color_transform.c adresinden erisebilirsiniz.

    YanıtlayınSil

Görüntü işleme ile ilgili yeni yazıları ve bu sitede yer alan yazıların güncellenmiş sürümlerini www.imlab.io veya cescript.github.io adreslerinden takip edebilirsiniz.

X